├── .gitignore ├── Config ├── DefaultEditor.ini ├── DefaultEditorPerProjectUserSettings.ini ├── DefaultEngine.ini └── DefaultGame.ini ├── Content └── LocalSimulation │ ├── Audio │ └── Physical_Materials │ │ ├── Metal.uasset │ │ └── Rubber.uasset │ ├── BluePrint │ ├── BP_DemoDisplay.uasset │ ├── BP_DemoDisplay_Enum.uasset │ ├── BP_DemoDisplay_Static.uasset │ └── RoomDescription.uasset │ ├── Examples │ └── TrainExample │ │ └── TrainExample_Blueprint.uasset │ ├── Maps │ └── Documentation.umap │ ├── Materials │ ├── DefaultTextMaterialTranslucentUnlit.uasset │ ├── M_DemoWall.uasset │ ├── M_DemoWall_Inst.uasset │ ├── M_DemoWall_Inst2.uasset │ ├── M_DemoWall_Inst_3.uasset │ ├── M_DemoWall_Inst_BaseFloor_Inst.uasset │ ├── M_DemoWall_Inst_NamePlate.uasset │ ├── M_Tech_Hex_Tile.uasset │ └── M_Tech_Hex_Tile_Pulse.uasset │ ├── Meshes │ ├── SM_Base.uasset │ ├── SM_Base2.uasset │ ├── SM_BaseRoom.uasset │ ├── SM_Base_FlatWall.uasset │ └── SM_NamePlate.uasset │ ├── Props │ ├── SM_Chair.uasset │ └── SM_CornerFrame.uasset │ ├── Shapes │ └── Shape_Cube.uasset │ └── StartHere │ └── BPInheritedLocalSimulationVolume.uasset ├── LICENCE ├── LocalSimulation.uproject ├── Plugins └── LocalPhysics │ ├── LocalPhysics.uplugin │ └── Source │ └── LocalPhysics │ ├── LocalPhysics.build.cs │ ├── Private │ ├── LocalPhysicsActor.cpp │ ├── LocalPhysicsContactPointRecorder.cpp │ ├── LocalPhysicsD6JointData.cpp │ ├── LocalPhysicsPlugin.cpp │ ├── LocalPhysicsSimulation.cpp │ └── LocalSimulationVolume.cpp │ └── Public │ ├── ILocalPhysicsPlugin.h │ ├── LocalPhysicsActor.h │ ├── LocalPhysicsActorHandle.h │ ├── LocalPhysicsCacheAllocator.h │ ├── LocalPhysicsConstraintAllocator.h │ ├── LocalPhysicsContactPair.h │ ├── LocalPhysicsContactPointRecorder.h │ ├── LocalPhysicsD6JointData.h │ ├── LocalPhysicsJoint.h │ ├── LocalPhysicsJointHandle.h │ ├── LocalPhysicsLinearBlockAllocator.h │ ├── LocalPhysicsModule.h │ ├── LocalPhysicsPersistentContactPairData.h │ ├── LocalPhysicsShape.h │ ├── LocalPhysicsSimulation.h │ └── LocalSimulationVolume.h ├── README.md └── Source ├── LocalSimulation.Target.cs ├── LocalSimulation ├── LocalSimulation.Build.cs ├── LocalSimulation.cpp ├── LocalSimulation.h ├── LocalSimulationGameModeBase.cpp └── LocalSimulationGameModeBase.h └── LocalSimulationEditor.Target.cs /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/* 2 | Binaries/* 3 | Saved/* 4 | Intermediate/* 5 | *.sln 6 | Plugins/LocalPhysics/Intermediate/* 7 | Plugins/LocalPhysics/Binaries/* 8 | LocalSimulation.VC.VC.opendb 9 | *.db -------------------------------------------------------------------------------- /Config/DefaultEditor.ini: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /Config/DefaultEditorPerProjectUserSettings.ini: -------------------------------------------------------------------------------- 1 | 2 | 3 | [/Script/SourceCodeAccess.SourceCodeAccessSettings] 4 | PreferredAccessor=VisualStudio2017 5 | 6 | -------------------------------------------------------------------------------- /Config/DefaultEngine.ini: -------------------------------------------------------------------------------- 1 | [URL] 2 | [/Script/EngineSettings.GameMapsSettings] 3 | EditorStartupMap=/Game/LocalSimulation/Maps/Documentation.Documentation 4 | GameDefaultMap=/Game/LocalSimulation/Maps/Documentation.Documentation 5 | GlobalDefaultGameMode="/Script/LocalSimulation.LocalSimulationGameMode" 6 | 7 | [/Script/HardwareTargeting.HardwareTargetingSettings] 8 | TargetedHardwareClass=Desktop 9 | AppliedTargetedHardwareClass=Desktop 10 | DefaultGraphicsPerformance=Maximum 11 | AppliedDefaultGraphicsPerformance=Maximum 12 | 13 | [/Script/Engine.PhysicsSettings] 14 | DefaultGravityZ=-980.000000 15 | DefaultTerminalVelocity=4000.000000 16 | DefaultFluidFriction=0.300000 17 | SimulateScratchMemorySize=262144 18 | RagdollAggregateThreshold=4 19 | TriangleMeshTriangleMinAreaThreshold=5.000000 20 | bEnableAsyncScene=False 21 | bEnableShapeSharing=False 22 | bEnablePCM=True 23 | bEnableStabilization=False 24 | bWarnMissingLocks=True 25 | bEnable2DPhysics=False 26 | LockedAxis=Invalid 27 | DefaultDegreesOfFreedom=Full3D 28 | BounceThresholdVelocity=200.000000 29 | FrictionCombineMode=Average 30 | RestitutionCombineMode=Average 31 | MaxAngularVelocity=3600.000000 32 | MaxDepenetrationVelocity=0.000000 33 | ContactOffsetMultiplier=0.010000 34 | MinContactOffset=0.000100 35 | MaxContactOffset=1.000000 36 | bSimulateSkeletalMeshOnDedicatedServer=True 37 | DefaultShapeComplexity=CTF_UseSimpleAndComplex 38 | bDefaultHasComplexCollision=True 39 | bSuppressFaceRemapTable=False 40 | bSupportUVFromHitResults=False 41 | bDisableActiveActors=False 42 | bDisableCCD=False 43 | MaxPhysicsDeltaTime=0.033333 44 | bSubstepping=False 45 | bSubsteppingAsync=False 46 | MaxSubstepDeltaTime=0.016667 47 | MaxSubsteps=6 48 | SyncSceneSmoothingFactor=0.000000 49 | AsyncSceneSmoothingFactor=0.990000 50 | InitialAverageFrameRate=0.016667 51 | 52 | 53 | -------------------------------------------------------------------------------- /Config/DefaultGame.ini: -------------------------------------------------------------------------------- 1 | [/Script/EngineSettings.GeneralProjectSettings] 2 | ProjectID=B9D3F1F74707373CFC290EA916EF3D22 3 | 4 | [StartupActions] 5 | bAddPacks=True 6 | InsertPack=(PackSource="StarterContent.upack,PackName="StarterContent") 7 | -------------------------------------------------------------------------------- /Content/LocalSimulation/Audio/Physical_Materials/Metal.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterlnewton/LocalSimulation/b0ed468b5550f6452393af368d5c6b75ea5cc7f2/Content/LocalSimulation/Audio/Physical_Materials/Metal.uasset -------------------------------------------------------------------------------- /Content/LocalSimulation/Audio/Physical_Materials/Rubber.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterlnewton/LocalSimulation/b0ed468b5550f6452393af368d5c6b75ea5cc7f2/Content/LocalSimulation/Audio/Physical_Materials/Rubber.uasset -------------------------------------------------------------------------------- /Content/LocalSimulation/BluePrint/BP_DemoDisplay.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterlnewton/LocalSimulation/b0ed468b5550f6452393af368d5c6b75ea5cc7f2/Content/LocalSimulation/BluePrint/BP_DemoDisplay.uasset -------------------------------------------------------------------------------- /Content/LocalSimulation/BluePrint/BP_DemoDisplay_Enum.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterlnewton/LocalSimulation/b0ed468b5550f6452393af368d5c6b75ea5cc7f2/Content/LocalSimulation/BluePrint/BP_DemoDisplay_Enum.uasset -------------------------------------------------------------------------------- /Content/LocalSimulation/BluePrint/BP_DemoDisplay_Static.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterlnewton/LocalSimulation/b0ed468b5550f6452393af368d5c6b75ea5cc7f2/Content/LocalSimulation/BluePrint/BP_DemoDisplay_Static.uasset -------------------------------------------------------------------------------- /Content/LocalSimulation/BluePrint/RoomDescription.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterlnewton/LocalSimulation/b0ed468b5550f6452393af368d5c6b75ea5cc7f2/Content/LocalSimulation/BluePrint/RoomDescription.uasset -------------------------------------------------------------------------------- /Content/LocalSimulation/Examples/TrainExample/TrainExample_Blueprint.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterlnewton/LocalSimulation/b0ed468b5550f6452393af368d5c6b75ea5cc7f2/Content/LocalSimulation/Examples/TrainExample/TrainExample_Blueprint.uasset -------------------------------------------------------------------------------- /Content/LocalSimulation/Maps/Documentation.umap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterlnewton/LocalSimulation/b0ed468b5550f6452393af368d5c6b75ea5cc7f2/Content/LocalSimulation/Maps/Documentation.umap -------------------------------------------------------------------------------- /Content/LocalSimulation/Materials/DefaultTextMaterialTranslucentUnlit.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterlnewton/LocalSimulation/b0ed468b5550f6452393af368d5c6b75ea5cc7f2/Content/LocalSimulation/Materials/DefaultTextMaterialTranslucentUnlit.uasset -------------------------------------------------------------------------------- /Content/LocalSimulation/Materials/M_DemoWall.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterlnewton/LocalSimulation/b0ed468b5550f6452393af368d5c6b75ea5cc7f2/Content/LocalSimulation/Materials/M_DemoWall.uasset -------------------------------------------------------------------------------- /Content/LocalSimulation/Materials/M_DemoWall_Inst.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterlnewton/LocalSimulation/b0ed468b5550f6452393af368d5c6b75ea5cc7f2/Content/LocalSimulation/Materials/M_DemoWall_Inst.uasset -------------------------------------------------------------------------------- /Content/LocalSimulation/Materials/M_DemoWall_Inst2.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterlnewton/LocalSimulation/b0ed468b5550f6452393af368d5c6b75ea5cc7f2/Content/LocalSimulation/Materials/M_DemoWall_Inst2.uasset -------------------------------------------------------------------------------- /Content/LocalSimulation/Materials/M_DemoWall_Inst_3.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterlnewton/LocalSimulation/b0ed468b5550f6452393af368d5c6b75ea5cc7f2/Content/LocalSimulation/Materials/M_DemoWall_Inst_3.uasset -------------------------------------------------------------------------------- /Content/LocalSimulation/Materials/M_DemoWall_Inst_BaseFloor_Inst.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterlnewton/LocalSimulation/b0ed468b5550f6452393af368d5c6b75ea5cc7f2/Content/LocalSimulation/Materials/M_DemoWall_Inst_BaseFloor_Inst.uasset -------------------------------------------------------------------------------- /Content/LocalSimulation/Materials/M_DemoWall_Inst_NamePlate.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterlnewton/LocalSimulation/b0ed468b5550f6452393af368d5c6b75ea5cc7f2/Content/LocalSimulation/Materials/M_DemoWall_Inst_NamePlate.uasset -------------------------------------------------------------------------------- /Content/LocalSimulation/Materials/M_Tech_Hex_Tile.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterlnewton/LocalSimulation/b0ed468b5550f6452393af368d5c6b75ea5cc7f2/Content/LocalSimulation/Materials/M_Tech_Hex_Tile.uasset -------------------------------------------------------------------------------- /Content/LocalSimulation/Materials/M_Tech_Hex_Tile_Pulse.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterlnewton/LocalSimulation/b0ed468b5550f6452393af368d5c6b75ea5cc7f2/Content/LocalSimulation/Materials/M_Tech_Hex_Tile_Pulse.uasset -------------------------------------------------------------------------------- /Content/LocalSimulation/Meshes/SM_Base.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterlnewton/LocalSimulation/b0ed468b5550f6452393af368d5c6b75ea5cc7f2/Content/LocalSimulation/Meshes/SM_Base.uasset -------------------------------------------------------------------------------- /Content/LocalSimulation/Meshes/SM_Base2.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterlnewton/LocalSimulation/b0ed468b5550f6452393af368d5c6b75ea5cc7f2/Content/LocalSimulation/Meshes/SM_Base2.uasset -------------------------------------------------------------------------------- /Content/LocalSimulation/Meshes/SM_BaseRoom.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterlnewton/LocalSimulation/b0ed468b5550f6452393af368d5c6b75ea5cc7f2/Content/LocalSimulation/Meshes/SM_BaseRoom.uasset -------------------------------------------------------------------------------- /Content/LocalSimulation/Meshes/SM_Base_FlatWall.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterlnewton/LocalSimulation/b0ed468b5550f6452393af368d5c6b75ea5cc7f2/Content/LocalSimulation/Meshes/SM_Base_FlatWall.uasset -------------------------------------------------------------------------------- /Content/LocalSimulation/Meshes/SM_NamePlate.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterlnewton/LocalSimulation/b0ed468b5550f6452393af368d5c6b75ea5cc7f2/Content/LocalSimulation/Meshes/SM_NamePlate.uasset -------------------------------------------------------------------------------- /Content/LocalSimulation/Props/SM_Chair.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterlnewton/LocalSimulation/b0ed468b5550f6452393af368d5c6b75ea5cc7f2/Content/LocalSimulation/Props/SM_Chair.uasset -------------------------------------------------------------------------------- /Content/LocalSimulation/Props/SM_CornerFrame.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterlnewton/LocalSimulation/b0ed468b5550f6452393af368d5c6b75ea5cc7f2/Content/LocalSimulation/Props/SM_CornerFrame.uasset -------------------------------------------------------------------------------- /Content/LocalSimulation/Shapes/Shape_Cube.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterlnewton/LocalSimulation/b0ed468b5550f6452393af368d5c6b75ea5cc7f2/Content/LocalSimulation/Shapes/Shape_Cube.uasset -------------------------------------------------------------------------------- /Content/LocalSimulation/StartHere/BPInheritedLocalSimulationVolume.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peterlnewton/LocalSimulation/b0ed468b5550f6452393af368d5c6b75ea5cc7f2/Content/LocalSimulation/StartHere/BPInheritedLocalSimulationVolume.uasset -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Peter L. Newton 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LocalSimulation.uproject: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "EngineAssociation": "4.16", 4 | "Category": "", 5 | "Description": "", 6 | "Modules": [ 7 | { 8 | "Name": "LocalSimulation", 9 | "Type": "Runtime", 10 | "LoadingPhase": "Default" 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /Plugins/LocalPhysics/LocalPhysics.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion" : 1, 3 | "Version" : 1, 4 | "VersionName" : "1.0", 5 | "FriendlyName" : "Local Mode Physics", 6 | "Description" : "Local Mode Physics adapting Immediate Mode Physx Integration", 7 | "Category" : "Physics", 8 | "CreatedBy" : "Peter L. Newton", 9 | "CreatedByURL" : "http://peterlnewton.com", 10 | "DocsURL" : "", 11 | "MarketplaceURL" : "", 12 | "SupportURL" : "", 13 | "EnabledByDefault" : true, 14 | "CanContainContent" : false, 15 | "IsBetaVersion" : false, 16 | "Installed" : false, 17 | "Modules" : 18 | [ 19 | { 20 | "Name" : "LocalPhysics", 21 | "Type" : "Runtime", 22 | "LoadingPhase" : "PreDefault" 23 | } 24 | ] 25 | } -------------------------------------------------------------------------------- /Plugins/LocalPhysics/Source/LocalPhysics/LocalPhysics.build.cs: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class LocalPhysics: ModuleRules 6 | { 7 | public LocalPhysics(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PrivateDependencyModuleNames.AddRange( 10 | new string[] { 11 | "Core", 12 | "CoreUObject", 13 | "Engine", 14 | "AnimGraphRuntime", 15 | "PhysX", 16 | "APEX" //This is not really needed, but the includes are all coupled. Not really an issue at the moment, could be broken out in the future 17 | 18 | } 19 | ); 20 | 21 | PrivateIncludePaths.AddRange( 22 | new string[] { 23 | "Runtime/LocalPhysics/Private" 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Plugins/LocalPhysics/Source/LocalPhysics/Private/LocalPhysicsActor.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "LocalPhysicsActor.h" 4 | #include "PhysicsPublic.h" 5 | #include "PhysicsEngine/BodySetup.h" 6 | 7 | namespace LocalPhysics 8 | { 9 | 10 | #if WITH_PHYSX 11 | void FActor::CreateGeometry(PxRigidActor* RigidActor, const PxTransform& ActorToBodyTM) 12 | { 13 | const uint32 NumShapes = RigidActor->getNbShapes(); 14 | TArray ActorShapes; 15 | ActorShapes.AddUninitialized(NumShapes); 16 | RigidActor->getShapes(ActorShapes.GetData(), sizeof(PxShape*)*NumShapes); 17 | PxTransform BodyToActorTM = ActorToBodyTM.getInverse(); 18 | 19 | Shapes.Empty(NumShapes); 20 | 21 | for(PxShape* Shape : ActorShapes) 22 | { 23 | if(!(Shape->getFlags() & PxShapeFlag::eSIMULATION_SHAPE)) 24 | { 25 | continue; 26 | } 27 | 28 | const PxTransform LocalPose = Shape->getLocalPose(); 29 | const PxTransform BodyLocalShape = BodyToActorTM * Shape->getLocalPose(); 30 | const PxGeometryHolder& GeomHolder = Shape->getGeometry(); 31 | const PxBounds3 Bounds = PxGeometryQuery::getWorldBounds(GeomHolder.any(), BodyLocalShape, /*inflation=*/1.f); 32 | const float BoundsMag = Bounds.getExtents().magnitude(); 33 | const PxVec3 BoundsCenter = Bounds.getCenter(); 34 | switch (GeomHolder.getType()) 35 | { 36 | case PxGeometryType::eSPHERE: Shapes.Emplace(BodyLocalShape, BoundsCenter, BoundsMag, new PxSphereGeometry(GeomHolder.sphere().radius)); break; 37 | case PxGeometryType::eCAPSULE: Shapes.Emplace(BodyLocalShape, BoundsCenter, BoundsMag, new PxCapsuleGeometry(GeomHolder.capsule().radius, GeomHolder.capsule().halfHeight)); break; 38 | case PxGeometryType::eBOX: Shapes.Emplace(BodyLocalShape, BoundsCenter, BoundsMag, new PxBoxGeometry(GeomHolder.box().halfExtents)); break; 39 | case PxGeometryType::eCONVEXMESH: Shapes.Emplace(BodyLocalShape, BoundsCenter, BoundsMag, new PxConvexMeshGeometry(GeomHolder.convexMesh().convexMesh, GeomHolder.convexMesh().scale, GeomHolder.convexMesh().meshFlags)); break; 40 | case PxGeometryType::eHEIGHTFIELD: Shapes.Emplace(BodyLocalShape, BoundsCenter, BoundsMag, new PxHeightFieldGeometry(GeomHolder.heightField().heightField, GeomHolder.heightField().heightFieldFlags, GeomHolder.heightField().heightScale, GeomHolder.heightField().rowScale, GeomHolder.heightField().columnScale)); break; 41 | case PxGeometryType::eTRIANGLEMESH: Shapes.Emplace(BodyLocalShape, BoundsCenter, BoundsMag, new PxTriangleMeshGeometry(GeomHolder.triangleMesh().triangleMesh, GeomHolder.triangleMesh().scale, GeomHolder.triangleMesh().meshFlags)); break; 42 | default: continue; //we don't bother with other types 43 | } 44 | } 45 | } 46 | 47 | void FActor::TerminateGeometry() 48 | { 49 | for(FShape& Shape : Shapes) 50 | { 51 | delete Shape.Geometry; 52 | } 53 | 54 | Shapes.Empty(); 55 | } 56 | #endif 57 | 58 | } 59 | 60 | -------------------------------------------------------------------------------- /Plugins/LocalPhysics/Source/LocalPhysics/Private/LocalPhysicsContactPointRecorder.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2017 Epic Games, Inc. All Rights Reserved. 2 | #include "LocalPhysicsActor.h" 3 | #include "LocalPhysicsContactPointRecorder.h" 4 | #include "LocalPhysicsContactPair.h" 5 | #include "LocalPhysicsSimulation.h" 6 | 7 | namespace LocalPhysics 8 | { 9 | 10 | bool FContactPointRecorder::recordContacts(const Gu::ContactPoint* ContactPoints, const PxU32 NumContacts, const PxU32 Index) 11 | { 12 | FContactPair ContactPair; 13 | ContactPair.DynamicActorDataIndex = DynamicActorDataIndex; 14 | ContactPair.OtherActorDataIndex = OtherActorDataIndex; 15 | ContactPair.StartContactIndex = Simulation.ContactPoints.Num(); 16 | ContactPair.NumContacts = NumContacts; 17 | ContactPair.PairIdx = PairIdx; 18 | 19 | for (PxU32 ContactIndex = 0; ContactIndex < NumContacts; ++ContactIndex) 20 | { 21 | //Fill in solver-specific data that our contact gen does not produce... 22 | 23 | Gu::ContactPoint NewPoint = ContactPoints[ContactIndex]; 24 | NewPoint.maxImpulse = PX_MAX_F32; 25 | NewPoint.targetVel = PxVec3(0.f); 26 | NewPoint.staticFriction = 0.7f; 27 | NewPoint.dynamicFriction = 0.7f; 28 | NewPoint.restitution = 0.3f; 29 | NewPoint.materialFlags = 0; 30 | Simulation.ContactPoints.Add(NewPoint); 31 | } 32 | 33 | Simulation.ContactPairs.Add(ContactPair); 34 | return true; 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /Plugins/LocalPhysics/Source/LocalPhysics/Private/LocalPhysicsD6JointData.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. 2 | #include "LocalPhysicsActor.h" 3 | #include "LocalPhysicsD6JointData.h" 4 | 5 | /****** 6 | ************ :( 7 | ///CODE COPIED DIRECTLY OUT OF PHYSX (until refactor) 8 | ************ 9 | ******/ 10 | 11 | #if WITH_PHYSX 12 | 13 | namespace LocalPhysics 14 | { 15 | 16 | D6JointData::D6JointData(PxD6Joint* Joint) 17 | : linearLimit(GPhysXSDK->getTolerancesScale(), 0.f) 18 | , twistLimit(0.f, 0.f) 19 | , swingLimit(0.f, 0.f) 20 | { 21 | PxRigidActor* Actor0 = nullptr; 22 | PxRigidActor* Actor1 = nullptr; 23 | Joint->getActors(Actor0, Actor1); 24 | 25 | auto GetCOM = [](PxRigidActor* Actor) -> PxTransform 26 | { 27 | if (Actor) 28 | { 29 | if(Actor->getType() == PxActorType::eRIGID_DYNAMIC || Actor->getType() == PxActorType::eARTICULATION_LINK) 30 | { 31 | return static_cast(Actor)->getCMassLocalPose(); 32 | } 33 | else 34 | { 35 | return static_cast(Actor)->getGlobalPose().getInverse(); 36 | } 37 | } 38 | 39 | return PxTransform(PxIdentity); 40 | }; 41 | 42 | c2b[0] = GetCOM(Actor0).transformInv(Joint->getLocalPose(PxJointActorIndex::eACTOR0)); 43 | c2b[1] = GetCOM(Actor1).transformInv(Joint->getLocalPose(PxJointActorIndex::eACTOR1)); 44 | 45 | linearLimit = Joint->getLinearLimit(); 46 | swingLimit = Joint->getSwingLimit(); 47 | twistLimit = Joint->getTwistLimit(); 48 | 49 | for(uint32 DriveIdx = 0; DriveIdx < PxD6Drive::eCOUNT; ++DriveIdx) 50 | { 51 | drive[DriveIdx] = Joint->getDrive((PxD6Drive::Enum)DriveIdx); 52 | } 53 | 54 | Joint->getDriveVelocity(driveLinearVelocity, driveAngularVelocity); 55 | drivePosition = Joint->getDrivePosition(); 56 | 57 | for(uint32 MotionIdx = 0; MotionIdx < PxD6Axis::eCOUNT; ++MotionIdx) 58 | { 59 | motion[MotionIdx] = Joint->getMotion((PxD6Axis::Enum)MotionIdx); 60 | } 61 | } 62 | 63 | bool IsActive(const D6JointData& JointData, const PxD6Drive::Enum Index) 64 | { 65 | const PxD6JointDrive& DriveData = JointData.drive[Index]; 66 | return DriveData.stiffness != 0 || DriveData.damping != 0; 67 | } 68 | 69 | void PrepareJointData(D6JointData& JointData) 70 | { 71 | //angular 72 | JointData.thSwingY = PxTan(JointData.swingLimit.yAngle / 2); 73 | JointData.thSwingZ = PxTan(JointData.swingLimit.zAngle / 2); 74 | JointData.thSwingPad = PxTan(JointData.swingLimit.contactDistance / 2); 75 | 76 | JointData.tqSwingY = PxTan(JointData.swingLimit.yAngle / 4); 77 | JointData.tqSwingZ = PxTan(JointData.swingLimit.zAngle / 4); 78 | JointData.tqSwingPad = PxTan(JointData.swingLimit.contactDistance / 4); 79 | 80 | JointData.tqTwistLow = PxTan(JointData.twistLimit.lower / 4); 81 | JointData.tqTwistHigh = PxTan(JointData.twistLimit.upper / 4); 82 | JointData.tqTwistPad = PxTan(JointData.twistLimit.contactDistance / 4); 83 | 84 | //linear 85 | JointData.driving = 0; 86 | JointData.limited = 0; 87 | JointData.locked = 0; 88 | 89 | for (PxU32 i = 0; i1e-6f); 151 | return (tan1 + tan2) / (1 - tan1*tan2); 152 | } 153 | 154 | PX_CUDA_CALLABLE PxF32 sqr(const PxF32 a) 155 | { 156 | return a * a; 157 | } 158 | 159 | PX_CUDA_CALLABLE PxReal tanHalf(PxReal sin, PxReal cos) 160 | { 161 | return sin / (1 + cos); 162 | } 163 | 164 | 165 | class ConstraintHelper 166 | { 167 | Px1DConstraint* mConstraints; 168 | Px1DConstraint* mCurrent; 169 | PxVec3 mRa, mRb; 170 | 171 | public: 172 | ConstraintHelper(Px1DConstraint* c, const PxVec3& ra, const PxVec3& rb) 173 | : mConstraints(c), mCurrent(c), mRa(ra), mRb(rb) {} 174 | 175 | 176 | // hard linear & angular 177 | void linearHard(const PxVec3& axis, PxReal posErr) 178 | { 179 | Px1DConstraint *c = linear(axis, posErr, PxConstraintSolveHint::eEQUALITY); 180 | c->flags |= Px1DConstraintFlag::eOUTPUT_FORCE; 181 | } 182 | 183 | void angularHard(const PxVec3& axis, PxReal posErr) 184 | { 185 | Px1DConstraint *c = angular(axis, posErr, PxConstraintSolveHint::eEQUALITY); 186 | c->flags |= Px1DConstraintFlag::eOUTPUT_FORCE; 187 | } 188 | 189 | // limited linear & angular 190 | void linearLimit(const PxVec3& axis, PxReal ordinate, PxReal limitValue, const PxJointLimitParameters& limit) 191 | { 192 | PxReal pad = limit.isSoft() ? 0 : limit.contactDistance; 193 | 194 | if (ordinate + pad > limitValue) 195 | addLimit(linear(axis, limitValue - ordinate, PxConstraintSolveHint::eNONE), limit); 196 | } 197 | 198 | void angularLimit(const PxVec3& axis, PxReal ordinate, PxReal limitValue, PxReal pad, const PxJointLimitParameters& limit) 199 | { 200 | if (limit.isSoft()) 201 | pad = 0; 202 | 203 | if (ordinate + pad > limitValue) 204 | addLimit(angular(axis, limitValue - ordinate, PxConstraintSolveHint::eNONE), limit); 205 | } 206 | 207 | 208 | void angularLimit(const PxVec3& axis, PxReal error, const PxJointLimitParameters& limit) 209 | { 210 | addLimit(angular(axis, error, PxConstraintSolveHint::eNONE), limit); 211 | } 212 | 213 | void halfAnglePair(PxReal halfAngle, PxReal lower, PxReal upper, PxReal pad, const PxVec3& axis, const PxJointLimitParameters& limit) 214 | { 215 | PX_ASSERT(lower upper - pad) 222 | angularLimit(axis, (upper - halfAngle) * 2, limit); 223 | } 224 | 225 | void quarterAnglePair(PxReal quarterAngle, PxReal lower, PxReal upper, PxReal pad, const PxVec3& axis, const PxJointLimitParameters& limit) 226 | { 227 | if (limit.isSoft()) 228 | pad = 0; 229 | 230 | PX_ASSERT(lower upper - pad) 234 | angularLimit(axis, (upper - quarterAngle) * 4, limit); 235 | } 236 | 237 | // driven linear & angular 238 | 239 | void linear(const PxVec3& axis, PxReal velTarget, PxReal error, const PxD6JointDrive& drive) 240 | { 241 | addDrive(linear(axis, error, PxConstraintSolveHint::eNONE), velTarget, drive); 242 | } 243 | 244 | void angular(const PxVec3& axis, PxReal velTarget, PxReal error, const PxD6JointDrive& drive, PxConstraintSolveHint::Enum hint = PxConstraintSolveHint::eNONE) 245 | { 246 | addDrive(angular(axis, error, hint), velTarget, drive); 247 | } 248 | 249 | 250 | PxU32 getCount() { return PxU32(mCurrent - mConstraints); } 251 | 252 | void prepareLockedAxes(const PxQuat& qA, const PxQuat& qB, const PxVec3& cB2cAp, PxU32 lin, PxU32 ang) 253 | { 254 | Px1DConstraint* current = mCurrent; 255 | if (ang) 256 | { 257 | PxQuat qB2qA = qA.getConjugate() * qB; 258 | 259 | PxVec3 row[3]; 260 | computeJacobianAxes(row, qA, qB); 261 | PxVec3 imp = qB2qA.getImaginaryPart(); 262 | if (ang & 1) angular(row[0], -imp.x, PxConstraintSolveHint::eEQUALITY, current++); 263 | if (ang & 2) angular(row[1], -imp.y, PxConstraintSolveHint::eEQUALITY, current++); 264 | if (ang & 4) angular(row[2], -imp.z, PxConstraintSolveHint::eEQUALITY, current++); 265 | } 266 | 267 | if (lin) 268 | { 269 | PxMat33 axes(qA); 270 | if (lin & 1) linear(axes[0], -cB2cAp[0], PxConstraintSolveHint::eEQUALITY, current++); 271 | if (lin & 2) linear(axes[1], -cB2cAp[1], PxConstraintSolveHint::eEQUALITY, current++); 272 | if (lin & 4) linear(axes[2], -cB2cAp[2], PxConstraintSolveHint::eEQUALITY, current++); 273 | } 274 | 275 | for (Px1DConstraint* front = mCurrent; front < current; front++) 276 | front->flags = Px1DConstraintFlag::eOUTPUT_FORCE; 277 | 278 | mCurrent = current; 279 | } 280 | 281 | Px1DConstraint *getConstraintRow() 282 | { 283 | return mCurrent++; 284 | } 285 | 286 | private: 287 | Px1DConstraint* linear(const PxVec3& axis, PxReal posErr, PxConstraintSolveHint::Enum hint) 288 | { 289 | Px1DConstraint* c = mCurrent++; 290 | 291 | c->solveHint = PxU16(hint); 292 | c->linear0 = axis; c->angular0 = mRa.cross(axis); 293 | c->linear1 = axis; c->angular1 = mRb.cross(axis); 294 | PX_ASSERT(c->linear0.isFinite()); 295 | PX_ASSERT(c->linear1.isFinite()); 296 | PX_ASSERT(c->angular0.isFinite()); 297 | PX_ASSERT(c->angular1.isFinite()); 298 | 299 | c->geometricError = posErr; 300 | 301 | return c; 302 | } 303 | 304 | Px1DConstraint* angular(const PxVec3& axis, PxReal posErr, PxConstraintSolveHint::Enum hint) 305 | { 306 | Px1DConstraint* c = mCurrent++; 307 | 308 | c->solveHint = PxU16(hint); 309 | c->linear0 = PxVec3(0); c->angular0 = axis; 310 | c->linear1 = PxVec3(0); c->angular1 = axis; 311 | 312 | c->geometricError = posErr; 313 | return c; 314 | } 315 | 316 | Px1DConstraint* linear(const PxVec3& axis, PxReal posErr, PxConstraintSolveHint::Enum hint, Px1DConstraint* c) 317 | { 318 | c->solveHint = PxU16(hint); 319 | c->linear0 = axis; c->angular0 = mRa.cross(axis); 320 | c->linear1 = axis; c->angular1 = mRb.cross(axis); 321 | PX_ASSERT(c->linear0.isFinite()); 322 | PX_ASSERT(c->linear1.isFinite()); 323 | PX_ASSERT(c->angular0.isFinite()); 324 | PX_ASSERT(c->angular1.isFinite()); 325 | 326 | c->geometricError = posErr; 327 | 328 | return c; 329 | } 330 | 331 | Px1DConstraint* angular(const PxVec3& axis, PxReal posErr, PxConstraintSolveHint::Enum hint, Px1DConstraint* c) 332 | { 333 | c->solveHint = PxU16(hint); 334 | c->linear0 = PxVec3(0.f); c->angular0 = axis; 335 | c->linear1 = PxVec3(0.f); c->angular1 = axis; 336 | 337 | c->geometricError = posErr; 338 | 339 | return c; 340 | } 341 | 342 | void addLimit(Px1DConstraint* c, const PxJointLimitParameters& limit) 343 | { 344 | PxU16 flags = PxU16(c->flags | Px1DConstraintFlag::eOUTPUT_FORCE); 345 | 346 | if (limit.isSoft()) 347 | { 348 | flags |= Px1DConstraintFlag::eSPRING; 349 | c->mods.spring.stiffness = limit.stiffness; 350 | c->mods.spring.damping = limit.damping; 351 | } 352 | else 353 | { 354 | c->solveHint = PxConstraintSolveHint::eINEQUALITY; 355 | c->mods.bounce.restitution = limit.restitution; 356 | c->mods.bounce.velocityThreshold = limit.bounceThreshold; 357 | if (c->geometricError>0) 358 | flags |= Px1DConstraintFlag::eKEEPBIAS; 359 | if (limit.restitution>0) 360 | flags |= Px1DConstraintFlag::eRESTITUTION; 361 | } 362 | 363 | c->flags = flags; 364 | c->minImpulse = 0; 365 | } 366 | 367 | void addDrive(Px1DConstraint* c, PxReal velTarget, const PxD6JointDrive& drive) 368 | { 369 | c->velocityTarget = velTarget; 370 | 371 | PxU16 flags = PxU16(c->flags | Px1DConstraintFlag::eSPRING | Px1DConstraintFlag::eHAS_DRIVE_LIMIT); 372 | if (drive.flags & PxD6JointDriveFlag::eACCELERATION) 373 | flags |= Px1DConstraintFlag::eACCELERATION_SPRING; 374 | c->flags = flags; 375 | c->mods.spring.stiffness = drive.stiffness; 376 | c->mods.spring.damping = drive.damping; 377 | 378 | c->minImpulse = -drive.forceLimit; 379 | c->maxImpulse = drive.forceLimit; 380 | 381 | PX_ASSERT(c->linear0.isFinite()); 382 | PX_ASSERT(c->angular0.isFinite()); 383 | } 384 | }; 385 | 386 | void separateSwingTwist(const PxQuat& q, PxQuat& swing, PxQuat& twist) 387 | { 388 | twist = q.x != 0.0f ? PxQuat(q.x, 0, 0, q.w).getNormalized() : PxQuat(PxIdentity); 389 | swing = q * twist.getConjugate(); 390 | } 391 | 392 | PX_CUDA_CALLABLE PxVec3 ellipseClamp(const PxVec3& point, const PxVec3& radii) 393 | { 394 | // This function need to be implemented in the header file because 395 | // it is included in a spu shader program. 396 | 397 | // finds the closest point on the ellipse to a given point 398 | 399 | // (p.y, p.z) is the input point 400 | // (e.y, e.z) are the radii of the ellipse 401 | 402 | // lagrange multiplier method with Newton/Halley hybrid root-finder. 403 | // see http://www.geometrictools.com/Documentation/DistancePointToEllipse2.pdf 404 | // for proof of Newton step robustness and initial estimate. 405 | // Halley converges much faster but sometimes overshoots - when that happens we take 406 | // a newton step instead 407 | 408 | // converges in 1-2 iterations where D&C works well, and it's good with 4 iterations 409 | // with any ellipse that isn't completely crazy 410 | 411 | const PxU32 MAX_ITERATIONS = 20; 412 | const PxReal convergenceThreshold = 1e-4f; 413 | 414 | // iteration requires first quadrant but we recover generality later 415 | 416 | PxVec3 q(0, PxAbs(point.y), PxAbs(point.z)); 417 | const PxReal tinyEps = 1e-6f; // very close to minor axis is numerically problematic but trivial 418 | if (radii.y >= radii.z) 419 | { 420 | if (q.z < tinyEps) 421 | return PxVec3(0, point.y > 0 ? radii.y : -radii.y, 0); 422 | } 423 | else 424 | { 425 | if (q.y < tinyEps) 426 | return PxVec3(0, 0, point.z > 0 ? radii.z : -radii.z); 427 | } 428 | 429 | PxVec3 denom, e2 = radii.multiply(radii), eq = radii.multiply(q); 430 | 431 | // we can use any initial guess which is > maximum(-e.y^2,-e.z^2) and for which f(t) is > 0. 432 | // this guess works well near the axes, but is weak along the diagonals. 433 | 434 | PxReal t = PxMax(eq.y - e2.y, eq.z - e2.z); 435 | 436 | for (PxU32 i = 0; i < MAX_ITERATIONS; i++) 437 | { 438 | denom = PxVec3(0, 1 / (t + e2.y), 1 / (t + e2.z)); 439 | PxVec3 denom2 = eq.multiply(denom); 440 | 441 | PxVec3 fv = denom2.multiply(denom2); 442 | PxReal f = fv.y + fv.z - 1; 443 | 444 | // although in exact arithmetic we are guaranteed f>0, we can get here 445 | // on the first iteration via catastrophic cancellation if the point is 446 | // very close to the origin. In that case we just behave as if f=0 447 | 448 | if (f < convergenceThreshold) 449 | return e2.multiply(point).multiply(denom); 450 | 451 | PxReal df = fv.dot(denom) * -2.0f; 452 | t = t - f / df; 453 | } 454 | 455 | // we didn't converge, so clamp what we have 456 | PxVec3 r = e2.multiply(point).multiply(denom); 457 | return r * PxRecipSqrt(sqr(r.y / radii.y) + sqr(r.z / radii.z)); 458 | } 459 | 460 | 461 | class ConeLimitHelper 462 | { 463 | public: 464 | ConeLimitHelper(PxReal tanQSwingY, PxReal tanQSwingZ, PxReal tanQPadding) 465 | : mTanQYMax(tanQSwingY), mTanQZMax(tanQSwingZ), mTanQPadding(tanQPadding) {} 466 | 467 | // whether the point is inside the (inwardly) padded cone - if it is, there's no limit 468 | // constraint 469 | 470 | bool contains(const PxVec3& tanQSwing) 471 | { 472 | PxReal tanQSwingYPadded = tanAdd(PxAbs(tanQSwing.y), mTanQPadding); 473 | PxReal tanQSwingZPadded = tanAdd(PxAbs(tanQSwing.z), mTanQPadding); 474 | return sqr(tanQSwingYPadded / mTanQYMax) + sqr(tanQSwingZPadded / mTanQZMax) <= 1; 475 | } 476 | 477 | PxVec3 clamp(const PxVec3& tanQSwing, 478 | PxVec3& normal) 479 | { 480 | PxVec3 p = ellipseClamp(tanQSwing, PxVec3(0, mTanQYMax, mTanQZMax)); 481 | normal = PxVec3(0, p.y / sqr(mTanQYMax), p.z / sqr(mTanQZMax)); 482 | #ifdef PX_PARANOIA_ELLIPSE_CHECK 483 | PxReal err = PxAbs(sqr(p.y / mTanQYMax) + sqr(p.z / mTanQZMax) - 1); 484 | PX_ASSERT(err<1e-3); 485 | #endif 486 | 487 | return p; 488 | } 489 | 490 | 491 | // input is a swing quat, such that swing.x = twist.y = twist.z = 0, q = swing * twist 492 | // The routine is agnostic to the sign of q.w (i.e. we don't need the minimal-rotation swing) 493 | 494 | // output is an axis such that positive rotation increases the angle outward from the 495 | // limit (i.e. the image of the x axis), the error is the sine of the angular difference, 496 | // positive if the twist axis is inside the cone 497 | 498 | bool getLimit(const PxQuat& swing, PxVec3& axis, PxReal& error) 499 | { 500 | PX_ASSERT(swing.w>0); 501 | PxVec3 twistAxis = swing.getBasisVector0(); 502 | PxVec3 tanQSwing = PxVec3(0, tanHalf(swing.z, swing.w), -tanHalf(swing.y, swing.w)); 503 | if (contains(tanQSwing)) 504 | return false; 505 | 506 | PxVec3 normal, clamped = clamp(tanQSwing, normal); 507 | 508 | // rotation vector and ellipse normal 509 | PxVec3 r(0, -clamped.z, clamped.y), d(0, -normal.z, normal.y); 510 | 511 | // the point on the cone defined by the tanQ swing vector r 512 | PxVec3 p(1.f, 0, 0); 513 | PxReal r2 = r.dot(r), a = 1 - r2, b = 1 / (1 + r2), b2 = b*b; 514 | PxReal v1 = 2 * a*b2; 515 | PxVec3 v2(a, 2 * r.z, -2 * r.y); // a*p + 2*r.cross(p); 516 | PxVec3 coneLine = v1 * v2 - p; // already normalized 517 | 518 | // the derivative of coneLine in the direction d 519 | PxReal rd = r.dot(d); 520 | PxReal dv1 = -4 * rd*(3 - r2)*b2*b; 521 | PxVec3 dv2(-2 * rd, 2 * d.z, -2 * d.y); 522 | 523 | PxVec3 coneNormal = v1 * dv2 + dv1 * v2; 524 | 525 | axis = coneLine.cross(coneNormal) / coneNormal.magnitude(); 526 | error = coneLine.cross(axis).dot(twistAxis); 527 | 528 | PX_ASSERT(PxAbs(axis.magnitude() - 1)<1e-5f); 529 | 530 | #ifdef PX_PARANOIA_ELLIPSE_CHECK 531 | bool inside = sqr(tanQSwing.y / mTanQYMax) + sqr(tanQSwing.z / mTanQZMax) <= 1; 532 | PX_ASSERT(inside && error>-1e-4f || !inside && error<1e-4f); 533 | #endif 534 | 535 | return true; 536 | } 537 | 538 | private: 539 | 540 | 541 | PxReal mTanQYMax, mTanQZMax, mTanQPadding; 542 | }; 543 | 544 | 545 | PxU32 D6JointSolverPrep(Px1DConstraint* constraints, 546 | PxVec3& body0WorldOffset, 547 | PxU32 maxConstraints, 548 | PxConstraintInvMassScale& invMassScale, 549 | const void* constantBlock, 550 | const PxTransform& bA2w, 551 | const PxTransform& bB2w) 552 | { 553 | PX_UNUSED(maxConstraints); 554 | 555 | PX_UNUSED(maxConstraints); 556 | const D6JointData& data = 557 | *reinterpret_cast(constantBlock); 558 | 559 | invMassScale = data.invMassScale; 560 | 561 | const PxU32 SWING1_FLAG = 1 << PxD6Axis::eSWING1, 562 | SWING2_FLAG = 1 << PxD6Axis::eSWING2, 563 | TWIST_FLAG = 1 << PxD6Axis::eTWIST; 564 | 565 | const PxU32 ANGULAR_MASK = SWING1_FLAG | SWING2_FLAG | TWIST_FLAG; 566 | const PxU32 LINEAR_MASK = 1 << PxD6Axis::eX | 1 << PxD6Axis::eY | 1 << PxD6Axis::eZ; 567 | 568 | const PxD6JointDrive* drives = data.drive; 569 | PxU32 locked = data.locked, limited = data.limited, driving = data.driving; 570 | 571 | PxTransform cA2w = bA2w.transform(data.c2b[0]); 572 | PxTransform cB2w = bB2w.transform(data.c2b[1]); 573 | 574 | body0WorldOffset = cB2w.p - bA2w.p; 575 | ConstraintHelper g(constraints, cB2w.p - bA2w.p, cB2w.p - bB2w.p); 576 | 577 | if (cA2w.q.dot(cB2w.q)<0) // minimum dist quat (equiv to flipping cB2bB.q, which we don't use anywhere) 578 | cB2w.q = -cB2w.q; 579 | 580 | PxTransform cB2cA = cA2w.transformInv(cB2w); 581 | 582 | PX_ASSERT(data.c2b[0].isValid()); 583 | PX_ASSERT(data.c2b[1].isValid()); 584 | PX_ASSERT(cA2w.isValid()); 585 | PX_ASSERT(cB2w.isValid()); 586 | PX_ASSERT(cB2cA.isValid()); 587 | 588 | PxMat33 cA2w_m(cA2w.q), cB2w_m(cB2w.q); 589 | 590 | // handy for swing computation 591 | PxVec3 bX = cB2w_m[0], aY = cA2w_m[1], aZ = cA2w_m[2]; 592 | 593 | if (driving & ((1 << PxD6Drive::eX) | (1 << PxD6Drive::eY) | (1 << PxD6Drive::eZ))) 594 | { 595 | // TODO: make drive unilateral if we are outside the limit 596 | PxVec3 posErr = data.drivePosition.p - cB2cA.p; 597 | for (PxU32 i = 0; i < 3; i++) 598 | { 599 | // -driveVelocity because velTarget is child (body1) - parent (body0) and Jacobian is 1 for body0 and -1 for parent 600 | if (driving & (1 << (PxD6Drive::eX + i))) 601 | g.linear(cA2w_m[i], -data.driveLinearVelocity[i], posErr[i], drives[PxD6Drive::eX + i]); 602 | } 603 | } 604 | 605 | if (driving & ((1 << PxD6Drive::eSLERP) | (1 << PxD6Drive::eSWING) | (1 << PxD6Drive::eTWIST))) 606 | { 607 | PxQuat d2cA_q = cB2cA.q.dot(data.drivePosition.q)>0 ? data.drivePosition.q : -data.drivePosition.q; 608 | 609 | const PxVec3& v = data.driveAngularVelocity; 610 | PxQuat delta = d2cA_q.getConjugate() * cB2cA.q; 611 | 612 | if (driving & (1 << PxD6Drive::eSLERP)) 613 | { 614 | PxVec3 velTarget = -cA2w.rotate(data.driveAngularVelocity); 615 | 616 | PxVec3 axis[3] = { PxVec3(1.f,0,0), PxVec3(0,1.f,0), PxVec3(0,0,1.f) }; 617 | 618 | if (drives[PxD6Drive::eSLERP].stiffness != 0) 619 | computeJacobianAxes(axis, cA2w.q * d2cA_q, cB2w.q); // converges faster if there is only velocity drive 620 | 621 | for (PxU32 i = 0; i < 3; i++) 622 | g.angular(axis[i], axis[i].dot(velTarget), -delta.getImaginaryPart()[i], drives[PxD6Drive::eSLERP], PxConstraintSolveHint::eSLERP_SPRING); 623 | } 624 | else 625 | { 626 | if (driving & (1 << PxD6Drive::eTWIST)) 627 | g.angular(bX, v.x, -2.0f * delta.x, drives[PxD6Drive::eTWIST]); 628 | 629 | if (driving & (1 << PxD6Drive::eSWING)) 630 | { 631 | PxVec3 err = delta.rotate(PxVec3(1.f, 0, 0)); 632 | 633 | if (!(locked & SWING1_FLAG)) 634 | g.angular(cB2w_m[1], v.y, err.z, drives[PxD6Drive::eSWING]); 635 | 636 | if (!(locked & SWING2_FLAG)) 637 | g.angular(cB2w_m[2], v.z, -err.y, drives[PxD6Drive::eSWING]); 638 | } 639 | } 640 | } 641 | 642 | if (limited & ANGULAR_MASK) 643 | { 644 | PxQuat swing, twist; 645 | separateSwingTwist(cB2cA.q, swing, twist); 646 | 647 | // swing limits: if just one is limited: if the other is free, we support 648 | // (-pi/2, +pi/2) limit, using tan of the half-angle as the error measure parameter. 649 | // If the other is locked, we support (-pi, +pi) limits using the tan of the quarter-angle 650 | // Notation: th == Ps::tanHalf, tq = tanQuarter 651 | 652 | if (limited & SWING1_FLAG && limited & SWING2_FLAG) 653 | { 654 | ConeLimitHelper coneHelper(data.tqSwingZ, data.tqSwingY, data.tqSwingPad); 655 | 656 | PxVec3 axis; 657 | PxReal error; 658 | if (coneHelper.getLimit(swing, axis, error)) 659 | g.angularLimit(cA2w.rotate(axis), error, data.swingLimit); 660 | } 661 | else 662 | { 663 | const PxJointLimitCone &limit = data.swingLimit; 664 | 665 | PxReal tqPad = data.tqSwingPad, thPad = data.thSwingPad; 666 | 667 | if (limited & SWING1_FLAG) 668 | { 669 | if (locked & SWING2_FLAG) 670 | { 671 | g.quarterAnglePair(tanHalf(swing.y, swing.w), -data.tqSwingY, data.tqSwingY, tqPad, aY, limit); 672 | } 673 | else 674 | { 675 | PxReal dot = -aZ.dot(bX); 676 | g.halfAnglePair(tanHalf(dot, 1 - dot*dot), -data.thSwingY, data.thSwingY, thPad, aZ.cross(bX), limit); 677 | } 678 | } 679 | if (limited & SWING2_FLAG) 680 | { 681 | if (locked & SWING1_FLAG) 682 | { 683 | g.quarterAnglePair(tanHalf(swing.z, swing.w), -data.tqSwingZ, data.tqSwingZ, tqPad, aZ, limit); 684 | } 685 | else 686 | { 687 | PxReal dot = aY.dot(bX); 688 | g.halfAnglePair(tanHalf(dot, 1 - dot*dot), -data.thSwingZ, data.thSwingZ, thPad, -aY.cross(bX), limit); 689 | } 690 | } 691 | } 692 | 693 | if (limited & TWIST_FLAG) 694 | { 695 | g.quarterAnglePair(tanHalf(twist.x, twist.w), data.tqTwistLow, data.tqTwistHigh, data.tqTwistPad, 696 | cB2w_m[0], data.twistLimit); 697 | } 698 | } 699 | 700 | if (limited & LINEAR_MASK) 701 | { 702 | PxVec3 limitDir = PxVec3(0); 703 | 704 | for (PxU32 i = 0; i < 3; i++) 705 | { 706 | if (limited & (1 << (PxD6Axis::eX + i))) 707 | limitDir += cA2w_m[i] * cB2cA.p[i]; 708 | } 709 | 710 | PxReal distance = limitDir.magnitude(); 711 | if (distance > data.linearMinDist) 712 | g.linearLimit(limitDir * (1.0f / distance), distance, data.linearLimit.value, data.linearLimit); 713 | } 714 | 715 | // we handle specially the case of just one swing dof locked 716 | 717 | PxU32 angularLocked = locked & ANGULAR_MASK; 718 | 719 | if (angularLocked == SWING1_FLAG) 720 | { 721 | g.angularHard(bX.cross(aZ), -bX.dot(aZ)); 722 | locked &= ~SWING1_FLAG; 723 | } 724 | else if (angularLocked == SWING2_FLAG) 725 | { 726 | locked &= ~SWING2_FLAG; 727 | g.angularHard(bX.cross(aY), -bX.dot(aY)); 728 | } 729 | 730 | g.prepareLockedAxes(cA2w.q, cB2w.q, cB2cA.p, locked & 7, locked >> 3); 731 | 732 | return g.getCount(); 733 | } 734 | 735 | /*PxMassProperties FLowLevelPhysicsEntity::ComputeMassProperties() const 736 | { 737 | //return PxRigidBodyExt::computeMassPropertiesFromGeometries(Geometries.GetData(), LocalTMs.GetData(), Geometries.Num()); 738 | }*/ 739 | 740 | } 741 | #endif -------------------------------------------------------------------------------- /Plugins/LocalPhysics/Source/LocalPhysics/Private/LocalPhysicsPlugin.cpp: -------------------------------------------------------------------------------- 1 | // Peter L. Newton - https://twitter.com/peterlnewton 2 | 3 | #include "LocalPhysicsActor.h" 4 | #include "ILocalPhysicsPlugin.h" 5 | 6 | 7 | class FLocalPhysicsPlugin : public ILocalPhysicsPlugin 8 | { 9 | 10 | }; 11 | 12 | IMPLEMENT_MODULE(FLocalPhysicsPlugin, LocalPhysics ) 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Plugins/LocalPhysics/Source/LocalPhysics/Private/LocalPhysicsSimulation.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. 2 | // Peter L. Newton - https://twitter.com/peterlnewton 3 | #include "LocalPhysicsActor.h" 4 | #include "LocalPhysicsSimulation.h" 5 | #include "LocalPhysicsActorHandle.h" 6 | #include "LocalPhysicsJointHandle.h" 7 | #include "LocalPhysicsContactPointRecorder.h" 8 | 9 | namespace LocalPhysics 10 | { 11 | 12 | FLocalSimulation::FLocalSimulation() 13 | { 14 | NumActiveSimulatedBodies = 0; 15 | NumSimulatedBodies = 0; 16 | NumKinematicBodies = 0; 17 | NumSimulatedShapesWithCollision = 0; 18 | NumContactHeaders = 0; 19 | NumJointHeaders = 0; 20 | NumActiveJoints = 0; 21 | NumPositionIterations = 1; 22 | NumVelocityIterations = 1; 23 | bDirtyJointData = false; 24 | bRecreateIterationCache = false; 25 | SimCount = 0; 26 | } 27 | 28 | bool FLocalSimulation::HandleAvailableToSimulate() const 29 | { 30 | return (ActorHandles.Num() > 0); 31 | } 32 | 33 | 34 | FLocalSimulation::~FLocalSimulation() 35 | { 36 | for(FActorHandle* ActorHandle : ActorHandles) 37 | { 38 | delete ActorHandle; 39 | } 40 | 41 | ActorHandles.Empty(); 42 | } 43 | 44 | void FLocalSimulation::SetNumActiveBodies(uint32 InNumActiveBodies) 45 | { 46 | NumActiveSimulatedBodies = InNumActiveBodies; 47 | bDirtyJointData = true; 48 | } 49 | 50 | #if WITH_PHYSX 51 | FJointHandle* FLocalSimulation::CreateJoint(PxD6Joint* Joint, FActorHandle* Actor1, FActorHandle* Actor2) 52 | { 53 | //TODO: make this robust 54 | if (Actor1 == nullptr) return nullptr; 55 | 56 | check(&Actor1->OwningSimulation == this); 57 | check(!Actor2 || &Actor2->OwningSimulation == this); //Actor2 can be null 58 | 59 | FJointHandle* NewHandle = nullptr; 60 | 61 | int32 JointDataIdx = JointData.Emplace(Joint); 62 | PrepareJointData(JointData[JointDataIdx]); 63 | 64 | if(!JointData[JointDataIdx].HasConstraints()) 65 | { 66 | JointData.RemoveAt(JointDataIdx, /*Count=*/1, /*bAllowShrinking=*/false); 67 | } 68 | else 69 | { 70 | Joints.Emplace(); 71 | FJoint& NewJoint = Joints[JointDataIdx]; 72 | //TODO: make dynamic vs static API more robust 73 | NewJoint.DynamicActor = Actor2; 74 | NewJoint.OtherActor = Actor1; 75 | 76 | NewHandle = new FJointHandle(*this, Joints.Num() - 1); 77 | JointHandles.Add(NewHandle); 78 | bDirtyJointData = true; 79 | } 80 | 81 | ValidateArrays(); 82 | 83 | return NewHandle; 84 | } 85 | 86 | void FLocalSimulation::SetIgnoreCollisionPairTable(const TArray& InIgnoreTable) 87 | { 88 | IgnoreCollisionPairTable.Reset(); 89 | for(const FIgnorePair& IgnorePair : InIgnoreTable) 90 | { 91 | TSet& IgnoreActorsA = IgnoreCollisionPairTable.FindOrAdd(IgnorePair.A); 92 | IgnoreActorsA.Add(IgnorePair.B); 93 | 94 | TSet& IgnoreActorsB = IgnoreCollisionPairTable.FindOrAdd(IgnorePair.B); 95 | IgnoreActorsB.Add(IgnorePair.A); 96 | } 97 | 98 | bRecreateIterationCache = true; 99 | } 100 | 101 | void FLocalSimulation::SetIgnoreCollisionActors(const TArray& InIgnoreCollisionActors) 102 | { 103 | IgnoreCollisionActors.Reset(); 104 | IgnoreCollisionActors.Append(InIgnoreCollisionActors); 105 | bRecreateIterationCache = true; 106 | } 107 | 108 | void FLocalSimulation::RemoveActor(FActorHandle* Handle) 109 | { 110 | if(Handle != nullptr) 111 | { 112 | // store ref to index, swap with last (should move to apporpiate position to maintain sorting) 113 | auto index = Handle->ActorDataIndex; 114 | 115 | // if we have mroe than 1 actor, swap positions to keep maintian sorting todo: sorting lol 116 | if(ActorHandles.Num() > 1) 117 | { 118 | if (ActorHandles[ActorHandles.Num() - 1] != nullptr) 119 | { 120 | // this allows us to remove without unsorting 121 | SwapActorData(ActorHandles.Num() - 1, index); 122 | 123 | // store new index 124 | index = ActorHandles.Num() - 1; 125 | } 126 | 127 | } 128 | 129 | // store temp before removal from array container 130 | auto temp = ActorHandles[Handle->ActorDataIndex]; 131 | 132 | // remove from handles (only removes it from array) 133 | ActorHandles.Remove(Handle); 134 | RigidBodiesData.RemoveAt(index); 135 | SolverBodiesData.RemoveAt(index); 136 | PendingAcceleration.RemoveAt(index); 137 | 138 | // do appropaite operation by type 139 | switch(temp->rigidBodyType) 140 | { 141 | case 0: 142 | // do nothing 143 | break; 144 | case 1: 145 | // remove from simulated bodies to maintain accurate count 146 | --NumSimulatedBodies; 147 | break; 148 | case 2: 149 | // remove from kinematic bodies to maintain accurate count 150 | --NumKinematicBodies; 151 | break; 152 | } 153 | 154 | // mark flags as dirty to resort joint/actor cache 155 | bDirtyJointData = true; 156 | bRecreateIterationCache = true; 157 | 158 | delete temp; 159 | } 160 | } 161 | 162 | void FLocalSimulation::RemoveJoint(FJointHandle* Handle) 163 | { 164 | if (Handle != nullptr) 165 | { 166 | // store ref to index, swap with last (should move to apporpiate position to maintain sorting) 167 | auto index = Handle->JointDataIndex; 168 | 169 | // will be done to maintain sorting todo: maintaining sorting 170 | if (JointHandles.Num() > 1) 171 | { 172 | if (JointHandles[JointHandles.Num() - 1] != nullptr) 173 | { 174 | // this allows us to remove without unsorting 175 | SwapJointData(JointHandles.Num() - 1, index); 176 | 177 | // store new index 178 | index = JointHandles.Num() - 1; 179 | } 180 | } 181 | 182 | // store temp before removal from array container 183 | auto temp = JointHandles[Handle->JointDataIndex]; 184 | 185 | // remove data associated with handle 186 | JointHandles.Remove(Handle); 187 | JointData.RemoveAt(index); 188 | 189 | // mark flags as dirty to resort joint/actor cache 190 | bDirtyJointData = true; 191 | bRecreateIterationCache = true; //new entity potentially re-orders bodies so our iteration cache becomes stale. TODO: can this be optimized for adding static cases that don't re-order? 192 | } 193 | } 194 | 195 | template 196 | uint32 FLocalSimulation:: CreateActor(PxRigidActor* RigidActor, const FTransform& WorldTM) 197 | { 198 | bDirtyJointData = true; //new entity potentially re-orders bodies so joint data becomes stale. TODO: can this be optimized for adding static cases that don't re-order? 199 | bRecreateIterationCache = true; //new entity potentially re-orders bodies so our iteration cache becomes stale. TODO: can this be optimized for adding static cases that don't re-order? 200 | 201 | FActor* NewEntity = new (Actors) FActor(); 202 | const uint32 ActorDataIndex = Actors.Num() - 1; 203 | 204 | FActorHandle* NewActorHandle = new FActorHandle(*this, ActorDataIndex); 205 | ActorHandles.Add(NewActorHandle); 206 | 207 | PxSolverBodyData* NewSolverBodyData = new (SolverBodiesData) PxSolverBodyData(); 208 | immediate::PxRigidBodyData* NewRigidBodyData = new (RigidBodiesData) immediate::PxRigidBodyData(); 209 | 210 | //Fill rigid body data based on the actor we've been given 211 | PxRigidBody* const RigidBody = ActorType == ECreateActorType::StaticActor ? nullptr : static_cast(RigidActor); //Note the const pointer - this is for templated code 212 | PxRigidDynamic* const RigidDynamic = ActorType == ECreateActorType::DynamicActor ? static_cast(RigidActor) : nullptr; //Note the const pointer - this is for templated code 213 | 214 | NewRigidBodyData->invMass = RigidDynamic ? RigidDynamic->getInvMass() : 0.f; 215 | NewRigidBodyData->angularVelocity = PxVec3(PxZero); 216 | NewRigidBodyData->maxDepenetrationVelocity = RigidDynamic ? RigidDynamic->getMaxDepenetrationVelocity() : PX_MAX_F32; 217 | NewRigidBodyData->invInertia = RigidDynamic ? RigidDynamic->getMassSpaceInvInertiaTensor() : PxVec3(PxZero); 218 | NewRigidBodyData->maxContactImpulse = RigidBody ? RigidBody->getMaxContactImpulse() : PX_MAX_F32; 219 | 220 | NewActorHandle->ActorToBody = /*RigidBody ? P2UTransform(RigidBody->getCMassLocalPose()) : */FTransform::Identity; //TODO: Why is this an unreal transform? 221 | NewRigidBodyData->body2World = U2PTransform(NewActorHandle->ActorToBody * WorldTM); 222 | 223 | NewRigidBodyData->linearDamping = RigidDynamic ? RigidDynamic->getLinearDamping() : 0.f; 224 | NewRigidBodyData->angularDamping = RigidDynamic ? RigidDynamic->getAngularDamping() : 0.f; 225 | NewRigidBodyData->maxLinearVelocitySq = PX_MAX_F32; //NOTE: rigid dynamics don't seem to have this max property. 226 | 227 | PendingAcceleration.AddZeroed(); 228 | 229 | if(RigidDynamic) 230 | { 231 | NewRigidBodyData->maxAngularVelocitySq = RigidDynamic->getMaxAngularVelocity(); 232 | NewRigidBodyData->maxAngularVelocitySq *= NewRigidBodyData->maxAngularVelocitySq; 233 | 234 | uint32 PositionIterations, VelocityIterations; 235 | RigidDynamic->getSolverIterationCounts(PositionIterations, VelocityIterations); 236 | 237 | NumPositionIterations = FMath::Max(PositionIterations, NumPositionIterations); 238 | NumVelocityIterations = FMath::Max(VelocityIterations, NumVelocityIterations); 239 | 240 | }else 241 | { 242 | NewRigidBodyData->maxAngularVelocitySq = PX_MAX_F32; 243 | } 244 | 245 | if (ActorType == ECreateActorType::StaticActor) 246 | { 247 | //If static we fill this struct here, for dynamic we fill during every call to simulate 248 | immediate::PxConstructStaticSolverBody(NewRigidBodyData->body2World, *NewSolverBodyData); 249 | } 250 | 251 | NewEntity->CreateGeometry(RigidActor, U2PTransform(NewActorHandle->ActorToBody)); 252 | 253 | ValidateArrays(); 254 | 255 | //We sort entities so that dynamics are first, kinematics second, and static third 256 | if (ActorType == ECreateActorType::DynamicActor) //dynamic first 257 | { 258 | if (NumSimulatedBodies < ActorDataIndex) //There's at least one kinematic/static entity so swap 259 | { 260 | const int32 FirstNonSimulatedIndex = NumSimulatedBodies; 261 | SwapActorData(FirstNonSimulatedIndex, ActorDataIndex); 262 | } 263 | 264 | ++NumSimulatedBodies; 265 | } 266 | else if(ActorType == ECreateActorType::KinematicActor) //then kinematics 267 | { 268 | const uint32 NumMobileBodies = NumKinematicBodies + NumSimulatedBodies; 269 | if (NumMobileBodies < ActorDataIndex) //There's at least one static entity so swap 270 | { 271 | const uint32 FirstStaticIndex = NumMobileBodies; 272 | SwapActorData(FirstStaticIndex, ActorDataIndex); 273 | } 274 | 275 | ++NumKinematicBodies; 276 | } 277 | 278 | // very poor way of setitng this up. 279 | NewActorHandle->rigidBodyType = (int)ActorType; 280 | return NewActorHandle->ActorDataIndex; 281 | } 282 | 283 | FActorHandle* FLocalSimulation::CreateDynamicActor(PxRigidDynamic* RigidDynamic, const FTransform& TM) 284 | { 285 | const uint32 ActorDataIndex = CreateActor(RigidDynamic, TM); 286 | NumActiveSimulatedBodies = NumSimulatedBodies; 287 | bDirtyJointData = true; 288 | 289 | return ActorHandles[ActorDataIndex]; 290 | } 291 | 292 | FActorHandle* FLocalSimulation::CreateKinematicActor(PxRigidBody* RigidBody, const FTransform& TM) 293 | { 294 | const uint32 ActorDataIndex = CreateActor(RigidBody, TM); 295 | return ActorHandles[ActorDataIndex]; 296 | } 297 | 298 | FActorHandle* FLocalSimulation::CreateStaticActor(PxRigidActor* RigidActor, const FTransform& TM) 299 | { 300 | const uint32 ActorDataIndex = CreateActor(RigidActor, TM); 301 | return ActorHandles[ActorDataIndex]; 302 | } 303 | #endif 304 | 305 | 306 | void FLocalSimulation::SwapActorData(uint32 Actor1DataIdx, uint32 Actor2DataIdx) 307 | { 308 | check(Actors.IsValidIndex(Actor1DataIdx)); 309 | check(Actors.IsValidIndex(Actor2DataIdx)); 310 | 311 | Swap(Actors[Actor1DataIdx], Actors[Actor2DataIdx]); 312 | Swap(ActorHandles[Actor1DataIdx], ActorHandles[Actor2DataIdx]); 313 | Swap(RigidBodiesData[Actor1DataIdx], RigidBodiesData[Actor2DataIdx]); 314 | Swap(SolverBodiesData[Actor1DataIdx], SolverBodiesData[Actor2DataIdx]); 315 | Swap(PendingAcceleration[Actor1DataIdx], PendingAcceleration[Actor2DataIdx]); 316 | 317 | //Update entity index on the handle 318 | ActorHandles[Actor1DataIdx]->ActorDataIndex = Actor1DataIdx; 319 | ActorHandles[Actor2DataIdx]->ActorDataIndex = Actor2DataIdx; 320 | bDirtyJointData = true; //reordering of bodies could lead to stale joint data 321 | bRecreateIterationCache = true; //reordering of bodies so we need to change iteration order potentially 322 | } 323 | 324 | void FLocalSimulation::SwapJointData(uint32 Joint1Idx, uint32 Joint2Idx) 325 | { 326 | check(JointData.IsValidIndex(Joint1Idx)); 327 | check(JointData.IsValidIndex(Joint2Idx)); 328 | 329 | Swap(JointData[Joint1Idx], JointData[Joint2Idx]); 330 | Swap(Joints[Joint1Idx], Joints[Joint2Idx]); 331 | Swap(JointHandles[Joint1Idx], JointHandles[Joint2Idx]); 332 | 333 | //Update joint index on the handle 334 | JointHandles[Joint1Idx]->JointDataIndex = Joint1Idx; 335 | JointHandles[Joint2Idx]->JointDataIndex = Joint2Idx; 336 | bDirtyJointData = true; //reordering of joints could lead to stale joint data 337 | } 338 | 339 | void FLocalSimulation::ValidateArrays() const 340 | { 341 | check(Actors.Num() == ActorHandles.Num()); 342 | #if WITH_PHYSX 343 | check(Actors.Num() == RigidBodiesData.Num()); 344 | check(Actors.Num() == SolverBodiesData.Num()); 345 | check(Actors.Num() == PendingAcceleration.Num()); 346 | check(Joints.Num() == JointData.Num()); 347 | check(Joints.Num() == JointHandles.Num()); 348 | #endif 349 | } 350 | 351 | /*static char* CacheHack = nullptr; 352 | int32 GCacheSumHack = 0; 353 | 354 | TAutoConsoleVariable GHackEvictCache(TEXT("p.ForceCacheEvict"), 1, TEXT("blah")); 355 | 356 | void FLocalSimulation::EvictCache() 357 | { 358 | if(GHackEvictCache.GetValueOnAnyThread() == 0) 359 | { 360 | return; 361 | } 362 | 363 | if(CacheHack == nullptr) 364 | { 365 | CacheHack = (char*) FMemory::Malloc(1024*1024*2); 366 | } 367 | 368 | const int32 NumCacheLines = 1024*1024*2/64; 369 | for(int i=0; i 0) 391 | { 392 | SCOPE_CYCLE_COUNTER(STAT_LocalSimulate); 393 | 394 | ++SimCount; 395 | 396 | ConstructSolverBodies(DeltaTime, Gravity); 397 | 398 | if(bRecreateIterationCache) 399 | { 400 | PrepareIterationCache(); 401 | } 402 | 403 | GenerateContacts(); 404 | 405 | BatchConstraints(); 406 | 407 | PrepareConstraints(DeltaTime); 408 | 409 | SolveAndIntegrate(DeltaTime); 410 | 411 | Workspace.Reset(); 412 | } 413 | 414 | //EvictCache(); 415 | } 416 | 417 | DECLARE_CYCLE_STAT(TEXT("ConstructSolverBodies"), STAT_LocalConstructSolverBodies, STATGROUP_LocalPhysics); 418 | 419 | void FLocalSimulation::ConstructSolverBodies(float DeltaTime, const FVector& Gravity) 420 | { 421 | #if WITH_PHYSX 422 | SCOPE_CYCLE_COUNTER(STAT_LocalConstructSolverBodies); 423 | const int32 NumBytes = Actors.Num() * sizeof(PxSolverBody); 424 | SolverBodies = (PxSolverBody*)Workspace.Alloc(NumBytes); 425 | 426 | for(uint32 BodyIdx = 0; BodyIdx < NumActiveSimulatedBodies; ++BodyIdx) 427 | { 428 | RigidBodiesData[BodyIdx].linearVelocity += PendingAcceleration[BodyIdx] * DeltaTime; 429 | } 430 | 431 | FMemory::Memzero(PendingAcceleration.GetData(), sizeof(PendingAcceleration[0]) * NumSimulatedBodies); 432 | 433 | FMemory::Memzero(SolverBodies, NumBytes); 434 | immediate::PxConstructSolverBodies(RigidBodiesData.GetData(), SolverBodiesData.GetData(), NumActiveSimulatedBodies, U2PVector(Gravity), DeltaTime); 435 | 436 | //kinematic have their velocities set directly, but we have to construct every frame because it could have changed 437 | immediate::PxConstructSolverBodies(RigidBodiesData.GetData() + NumActiveSimulatedBodies, SolverBodiesData.GetData() + NumActiveSimulatedBodies, NumKinematicBodies + (NumSimulatedBodies - NumActiveSimulatedBodies), PxVec3(PxZero), DeltaTime); 438 | #endif 439 | } 440 | 441 | DECLARE_CYCLE_STAT(TEXT("PrepareIterationCache"), STAT_LocalPrepareIterationCache, STATGROUP_LocalPhysics); 442 | 443 | void FLocalSimulation::PrepareIterationCache() 444 | { 445 | #if WITH_PHYSX 446 | SCOPE_CYCLE_COUNTER(STAT_LocalPrepareIterationCache); 447 | // Iteration cache is not yet available, so we need to iterate over bodies and prepare the data as needed 448 | const int32 NumActors = Actors.Num(); 449 | ShapeSOA.LocalTMs.Empty(NumActors); 450 | ShapeSOA.Geometries.Empty(NumActors); 451 | ShapeSOA.Bounds.Empty(NumActors); 452 | ShapeSOA.OwningActors.Empty(NumActors); 453 | ShapeSOA.BoundsOffsets.Empty(NumActors); 454 | 455 | int32 NumShapes = 0; 456 | NumSimulatedShapesWithCollision = 0; 457 | 458 | //flatten out all shapes of actors that have collision 459 | for(int32 ActorIdx = 0; ActorIdx < NumActors; ++ActorIdx) 460 | { 461 | const FActor& Actor = Actors[ActorIdx]; 462 | if(IgnoreCollisionActors.Contains(ActorHandles[ActorIdx])) 463 | { 464 | if (ActorIdx+1 == NumSimulatedBodies) 465 | { 466 | NumSimulatedShapesWithCollision = NumShapes; 467 | } 468 | 469 | continue; 470 | } 471 | 472 | for(const FShape& Shape : Actor.Shapes) 473 | { 474 | ShapeSOA.LocalTMs.Add(Shape.LocalTM); 475 | ShapeSOA.Geometries.Add(Shape.Geometry); 476 | ShapeSOA.Bounds.Add(Shape.BoundsMagnitude); 477 | ShapeSOA.BoundsOffsets.Add(Shape.BoundsOffset); 478 | ShapeSOA.OwningActors.Add(ActorIdx); 479 | ++NumShapes; 480 | } 481 | 482 | if (ActorIdx+1 == NumSimulatedBodies) 483 | { 484 | NumSimulatedShapesWithCollision = NumShapes; 485 | } 486 | } 487 | 488 | //build cache of which shapes to skip 489 | int32 IterationCount = 0; 490 | SkipCollisionCache.Empty(NumShapes); 491 | 492 | for(uint32 SimShapeIdx = 0; SimShapeIdx < NumSimulatedShapesWithCollision; ++SimShapeIdx) 493 | { 494 | const int32 SimActorIdx = ShapeSOA.OwningActors[SimShapeIdx]; 495 | const TSet* IgnoreActorsForSimulated = IgnoreCollisionPairTable.Find(ActorHandles[SimActorIdx]); 496 | 497 | for(int32 OtherShapeIdx = SimShapeIdx + 1; OtherShapeIdx < NumShapes; ++OtherShapeIdx) 498 | { 499 | const int32 OtherActorIdx = ShapeSOA.OwningActors[OtherShapeIdx]; 500 | 501 | if(SimActorIdx == OtherActorIdx || (IgnoreActorsForSimulated && IgnoreActorsForSimulated->Find(ActorHandles[OtherActorIdx]))) 502 | { 503 | SkipCollisionCache.Add(IterationCount++); 504 | } 505 | else 506 | { 507 | ++IterationCount; 508 | } 509 | } 510 | } 511 | 512 | #if PERSISTENT_CONTACT_PAIRS 513 | const int32 NumPairs = (NumShapes) * (NumShapes - 1) / 2; 514 | ShapeSOA.ContactPairData.Empty(NumPairs); 515 | ShapeSOA.ContactPairData.AddZeroed(NumPairs); 516 | #endif 517 | 518 | bRecreateIterationCache = false; 519 | #endif 520 | } 521 | 522 | DECLARE_CYCLE_STAT(TEXT("GenerateContacts"), STAT_LocalGenerateContacts, STATGROUP_LocalPhysics); 523 | 524 | void FLocalSimulation::GenerateContacts() 525 | { 526 | #if WITH_PHYSX 527 | SCOPE_CYCLE_COUNTER(STAT_LocalGenerateContacts); 528 | 529 | //narrow phase, find actual contact points 530 | CacheAllocator.Reset(); 531 | 532 | ContactPairs.Reset(); 533 | ContactPoints.Reset(); 534 | 535 | const int32 NumShapes = ShapeSOA.LocalTMs.Num(); 536 | PxTransform* ShapeWorldTMs = (PxTransform*)Workspace.Alloc(sizeof(PxTransform) * NumShapes); 537 | for(int32 ShapeIdx = 0; ShapeIdx < NumShapes; ++ShapeIdx) 538 | { 539 | const immediate::PxRigidBodyData& Body = RigidBodiesData[ShapeSOA.OwningActors[ShapeIdx]]; 540 | ShapeWorldTMs[ShapeIdx] = Body.body2World * ShapeSOA.LocalTMs[ShapeIdx]; 541 | } 542 | 543 | int32 IterationCount = 0; 544 | int32 PotentialPairCount = 0; 545 | const int32 IterationCacheSize = SkipCollisionCache.Num(); 546 | int32 CurrentIterationCacheIdx = 0; 547 | const int32* SkipCollisionCacheRaw = SkipCollisionCache.GetData(); 548 | 549 | for(uint32 SimShapeIdx = 0; SimShapeIdx < NumSimulatedShapesWithCollision; ++SimShapeIdx) 550 | { 551 | const float SimulatedRadius = ShapeSOA.Bounds[SimShapeIdx]; 552 | const PxTransform& SimulatedShapeTM = ShapeWorldTMs[SimShapeIdx]; 553 | const PxVec3 SimulatedBoundsOffset = ShapeSOA.BoundsOffsets[SimShapeIdx]; 554 | const PxVec3 SimulatedShapeBoundsOrigin = SimulatedShapeTM.transform(SimulatedBoundsOffset); 555 | 556 | const uint32 SimulatedActorIdx = ShapeSOA.OwningActors[SimShapeIdx]; 557 | const PxGeometry* SimulatedGeometry = ShapeSOA.Geometries[SimShapeIdx]; 558 | 559 | if(SimulatedActorIdx >= NumActiveSimulatedBodies) 560 | { 561 | break; 562 | } 563 | 564 | for(int32 OtherShapeIdx = SimShapeIdx + 1; OtherShapeIdx < NumShapes; ++OtherShapeIdx) 565 | { 566 | if(CurrentIterationCacheIdx < IterationCacheSize && SkipCollisionCache[CurrentIterationCacheIdx] == IterationCount++) 567 | { 568 | ++CurrentIterationCacheIdx; 569 | continue; 570 | } 571 | 572 | const uint32 OtherActorIdx = ShapeSOA.OwningActors[OtherShapeIdx]; 573 | if(OtherActorIdx >= NumSimulatedBodies && OtherActorIdx < NumSimulatedBodies) 574 | { 575 | continue; 576 | } 577 | 578 | PxCache* InCache; 579 | const int32 PotentialPairIdx = PotentialPairCount++; 580 | #if PERSISTENT_CONTACT_PAIRS 581 | FPersistentContactPairData& PersistentPairData = ShapeSOA.ContactPairData[PotentialPairIdx]; 582 | InCache = &PersistentPairData.Cache; 583 | 584 | if(PersistentPairData.SimCount + 1 != SimCount) //Note: in the case where we roll over to 0 does this matter? Could use % but seems unneeded for now 585 | { 586 | PersistentPairData.Clear(); 587 | } 588 | #else 589 | PxCache Cache; 590 | InCache = &Cache; 591 | #endif 592 | const float OtherRadius = ShapeSOA.Bounds[OtherShapeIdx]; 593 | const float TotalRadius = (SimulatedRadius + OtherRadius); 594 | const float TotalRadius2 = TotalRadius * TotalRadius; 595 | const PxTransform& OtherShapeTM = ShapeWorldTMs[OtherShapeIdx]; 596 | const PxVec3 OtherShapeBoundsOrigin = OtherShapeTM.transform(ShapeSOA.BoundsOffsets[OtherShapeIdx]); 597 | 598 | const float Distance2 = (SimulatedShapeBoundsOrigin - OtherShapeBoundsOrigin).magnitudeSquared(); 599 | 600 | if ( Distance2 > TotalRadius2) 601 | { 602 | #if PERSISTENT_CONTACT_PAIRS 603 | PersistentPairData.Clear(); 604 | #endif 605 | continue; //no intersection so skip narrow phase 606 | } 607 | 608 | FContactPointRecorder ContactRecorder(*this, SimulatedActorIdx, OtherActorIdx, PotentialPairIdx); 609 | if (!immediate::PxGenerateContacts(&SimulatedGeometry, &ShapeSOA.Geometries[OtherShapeIdx], &SimulatedShapeTM, &OtherShapeTM, InCache, 1, ContactRecorder, 4.f, 1.f, 100.f, CacheAllocator)) 610 | { 611 | #if PERSISTENT_CONTACT_PAIRS 612 | //no contacts found so clear friction 613 | PersistentPairData.Frictions = nullptr; 614 | PersistentPairData.NumFrictions = 0; 615 | #endif 616 | } 617 | } 618 | } 619 | #endif 620 | } 621 | 622 | bool GBatchJoints = false; //TODO: temp hack to fix crash in Local mode due to joint batching 623 | 624 | DECLARE_CYCLE_STAT(TEXT("BatchConstraints"), STAT_LocalBatchConstraints, STATGROUP_LocalPhysics); 625 | 626 | void FLocalSimulation::BatchConstraints() 627 | { 628 | #if WITH_PHYSX 629 | SCOPE_CYCLE_COUNTER(STAT_LocalBatchConstraints); 630 | 631 | const int32 NumContactPairs = ContactPairs.Num(); 632 | const int32 NumJoints = JointData.Num(); 633 | const int32 NumConstraints = NumJoints + NumContactPairs; 634 | OrderedDescriptors.SetNum(NumConstraints); //Ordered descriptors are semi-persistent. We only reorder joint constraints when needed, but we always update contact constraints which come at the end of the array. 635 | BatchHeaders.SetNum(NumConstraints); //batch headers follow the same approach 636 | 637 | // Joint constraints if needed 638 | if (bDirtyJointData) 639 | { 640 | NumActiveJoints = 0; 641 | if(NumJoints > 0) 642 | { 643 | PxSolverConstraintDesc* JointDescriptors = (PxSolverConstraintDesc*)Workspace.Alloc(sizeof(PxSolverConstraintDesc) * NumJoints); 644 | 645 | for (int32 JointIdx = 0; JointIdx < NumJoints; ++JointIdx) 646 | { 647 | PxSolverConstraintDesc& JointDescriptor = JointDescriptors[NumActiveJoints]; 648 | 649 | const FJoint& Joint = Joints[JointIdx]; 650 | const PxU32 DynamicActorIdx = Joint.DynamicActor->ActorDataIndex; 651 | const PxU32 OtherActorIdx = Joint.OtherActor != nullptr ? Joint.OtherActor->ActorDataIndex : INDEX_NONE; 652 | 653 | if( (DynamicActorIdx >= NumActiveSimulatedBodies && DynamicActorIdx < NumSimulatedBodies) || (OtherActorIdx >= NumActiveSimulatedBodies && OtherActorIdx < NumSimulatedBodies) ) 654 | { 655 | continue; 656 | } 657 | 658 | JointDescriptor.bodyA = &SolverBodies[DynamicActorIdx]; 659 | JointDescriptor.bodyB = OtherActorIdx != INDEX_NONE ? &SolverBodies[OtherActorIdx] : nullptr; 660 | 661 | JointDescriptor.bodyADataIndex = PxU16(DynamicActorIdx); 662 | JointDescriptor.bodyBDataIndex = PxU16(OtherActorIdx); 663 | JointDescriptor.linkIndexA = PxSolverConstraintDesc::NO_LINK; 664 | JointDescriptor.linkIndexB = PxSolverConstraintDesc::NO_LINK; 665 | JointDescriptor.writeBack = nullptr; 666 | 667 | JointDescriptor.constraint = (PxU8*)JointHandles[JointIdx]; 668 | JointDescriptor.constraintLengthOver16 = PxSolverConstraintDesc::eJOINT_CONSTRAINT; 669 | 670 | ++NumActiveJoints; 671 | } 672 | 673 | if(GBatchJoints) 674 | { 675 | NumJointHeaders = physx::immediate::PxBatchConstraints(JointDescriptors, NumActiveJoints, SolverBodies, Actors.Num(), BatchHeaders.GetData(), OrderedDescriptors.GetData()); 676 | } 677 | else 678 | { 679 | NumJointHeaders = NumActiveJoints; 680 | OrderedDescriptors.AddUninitialized(NumJointHeaders); 681 | FMemory::Memcpy(OrderedDescriptors.GetData(), JointDescriptors, sizeof(OrderedDescriptors[0]) * NumJointHeaders); 682 | 683 | for(uint32 Offset = 0; Offset < NumActiveJoints; ++Offset) 684 | { 685 | PxConstraintBatchHeader& BatchHeader = BatchHeaders[Offset]; 686 | BatchHeader.mStartIndex = Offset; 687 | BatchHeader.mStride = 1; 688 | } 689 | } 690 | 691 | 692 | //ensure D6 joint data and low level joint data are matching the Ordered descriptors 693 | for(uint32 DescriptorIdx = 0; DescriptorIdx < NumActiveJoints; ++DescriptorIdx) 694 | { 695 | FJointHandle* JointHandle = (FJointHandle*) OrderedDescriptors[DescriptorIdx].constraint; 696 | if(JointHandle->JointDataIndex != DescriptorIdx) 697 | { 698 | //The joint or the descriptor have moved - either way fix it 699 | SwapJointData(DescriptorIdx, JointHandle->JointDataIndex); 700 | } 701 | } 702 | } 703 | else 704 | { 705 | NumJointHeaders = 0; 706 | } 707 | } 708 | 709 | if(NumContactPairs > 0) 710 | { 711 | //Contact constraints 712 | PxSolverConstraintDesc* ContactDescriptors = (PxSolverConstraintDesc*)Workspace.Alloc(sizeof(PxSolverConstraintDesc) * NumContactPairs); 713 | for (int32 ContactPairIdx = 0; ContactPairIdx < NumContactPairs; ++ContactPairIdx) 714 | { 715 | const FContactPair& ContactPair = ContactPairs[ContactPairIdx]; 716 | PxSolverConstraintDesc& ContactDescriptor = ContactDescriptors[ContactPairIdx]; 717 | 718 | //Set body pointers and bodyData idxs 719 | ContactDescriptor.bodyA = &SolverBodies[ContactPair.DynamicActorDataIndex]; 720 | ContactDescriptor.bodyB = &SolverBodies[ContactPair.OtherActorDataIndex]; 721 | 722 | ContactDescriptor.bodyADataIndex = PxU16(ContactPair.DynamicActorDataIndex); 723 | ContactDescriptor.bodyBDataIndex = PxU16(ContactPair.OtherActorDataIndex); 724 | ContactDescriptor.linkIndexA = PxSolverConstraintDesc::NO_LINK; 725 | ContactDescriptor.linkIndexB = PxSolverConstraintDesc::NO_LINK; 726 | ContactDescriptor.writeBack = nullptr; 727 | 728 | ContactDescriptor.constraint = (PxU8*)(&ContactPair); //Need to save this off for later when the constraint descriptors get re-ordered 729 | ContactDescriptor.constraintLengthOver16 = PxSolverConstraintDesc::eCONTACT_CONSTRAINT; 730 | } 731 | 732 | NumContactHeaders = physx::immediate::PxBatchConstraints(ContactDescriptors, NumContactPairs, SolverBodies, Actors.Num(), BatchHeaders.GetData() + NumJointHeaders, OrderedDescriptors.GetData() + NumActiveJoints); 733 | } 734 | else 735 | { 736 | NumContactHeaders = 0; 737 | } 738 | 739 | #endif 740 | } 741 | 742 | //float GReadCacheHack = 0.f; 743 | 744 | DECLARE_CYCLE_STAT(TEXT("PrepareConstraints"), STAT_LocalPrepareConstraints, STATGROUP_LocalPhysics); 745 | 746 | void FLocalSimulation::PrepareConstraints(float DeltaTime) 747 | { 748 | #if WITH_PHYSX 749 | SCOPE_CYCLE_COUNTER(STAT_LocalPrepareConstraints); 750 | 751 | //Prepares constraint data based on latest position data and contact pairs 752 | const int32 NumBodies = Actors.Num(); 753 | const float InvDeltaTime = 1.f / DeltaTime; 754 | ConstraintAllocator.Reset(); 755 | 756 | PxSolverConstraintPrepDesc JointConstraints[4]; 757 | Px1DConstraint Rows[24]; //24 rows because we have at most 6 rows per joint and we could be solving at most 4 joints in one batch 758 | 759 | /*FPlatformMisc::Prefetch(OrderedDescriptors.GetData()); 760 | FPlatformMisc::Prefetch(BatchHeaders.GetData()); 761 | FPlatformMisc::Prefetch(SolverBodiesData.GetData()); 762 | FPlatformMisc::Prefetch(JointData.GetData());*/ 763 | 764 | const int32 NumMobileBodies = NumSimulatedBodies + NumKinematicBodies; 765 | 766 | //Joint constraints 767 | for (PxU32 HeaderIdx = 0; HeaderIdx < NumJointHeaders; ++HeaderIdx) 768 | { 769 | PxConstraintBatchHeader& Header = BatchHeaders[HeaderIdx]; 770 | Header.mConstraintType = PxSolverConstraintDesc::eJOINT_CONSTRAINT; //Since joint headers are cached and solver re-uses this we have to reset it every time 771 | 772 | FMemory::Memzero(Rows, sizeof(Rows)); //TODO: do we actually need to do this? 773 | for (Px1DConstraint& Constraint : Rows) 774 | { 775 | Constraint.minImpulse = -FLT_MAX; 776 | Constraint.maxImpulse = FLT_MAX; 777 | } 778 | 779 | int32 CurRows = 0; 780 | 781 | for (PxU32 BatchInnerIdx = 0; BatchInnerIdx < Header.mStride; ++BatchInnerIdx) 782 | { 783 | const PxU32 DescriptorIdx = Header.mStartIndex + BatchInnerIdx; 784 | //FPlatformMisc::PrefetchBlock(&JointData[DescriptorIdx], 128); 785 | //GReadCacheHack = JointData[DescriptorIdx].invMassScale.angular0; 786 | 787 | PxSolverConstraintDesc& OrderedDescriptor = OrderedDescriptors[DescriptorIdx]; 788 | PxSolverConstraintPrepDesc& JointDescriptor = JointConstraints[BatchInnerIdx]; 789 | 790 | //TODO: is it worth caching some of this? Most of the data doesn't change each frame 791 | JointDescriptor.body0 = OrderedDescriptor.bodyA; 792 | JointDescriptor.body1 = OrderedDescriptor.bodyB; 793 | JointDescriptor.data0 = &SolverBodiesData[OrderedDescriptor.bodyADataIndex]; 794 | JointDescriptor.data1 = &SolverBodiesData[OrderedDescriptor.bodyBDataIndex]; 795 | 796 | JointDescriptor.bodyFrame0 = JointDescriptor.data0->body2World; 797 | JointDescriptor.bodyFrame1 = JointDescriptor.data1->body2World; 798 | 799 | JointDescriptor.bodyState0 = PxSolverConstraintPrepDescBase::eDYNAMIC_BODY; 800 | JointDescriptor.bodyState1 = JointDescriptor.body1 == nullptr ? PxSolverConstraintPrepDescBase::eSTATIC_BODY : (OrderedDescriptor.bodyBDataIndex < NumSimulatedBodies ? PxSolverConstraintPrepDescBase::eDYNAMIC_BODY : PxSolverConstraintPrepDescBase::eSTATIC_BODY); 801 | JointDescriptor.desc = &OrderedDescriptor; 802 | JointDescriptor.mInvMassScales.angular0 = JointDescriptor.mInvMassScales.angular1 = JointDescriptor.mInvMassScales.linear0 = JointDescriptor.mInvMassScales.linear1 = 1.f; 803 | JointDescriptor.writeback = NULL; 804 | //Constraint.getBreakForce(JointDescriptor.linBreakForce, JointDescriptor.angBreakForce); 805 | JointDescriptor.linBreakForce = FLT_MAX; 806 | JointDescriptor.angBreakForce = FLT_MAX; 807 | 808 | JointDescriptor.minResponseThreshold = 0;//Constraint.getMinResponseThreshold(); 809 | JointDescriptor.disablePreprocessing = true;//!!(Constraint.getFlags() & PxConstraintFlag::eDISABLE_PREPROCESSING); 810 | JointDescriptor.improvedSlerp = true;//!!(Constraint.getFlags() & PxConstraintFlag::eIMPROVED_SLERP); 811 | JointDescriptor.driveLimitsAreForces = false;//!!(Constraint.getFlags() & PxConstraintFlag::eDRIVE_LIMITS_ARE_FORCES); 812 | 813 | PxVec3 Body0WorldOffset; 814 | PxConstraintInvMassScale InvMassScale; 815 | const D6JointData& Joint = JointData[DescriptorIdx]; 816 | uint32 NumRows = D6JointSolverPrep(&Rows[CurRows], Body0WorldOffset, 0, InvMassScale, (const void*)&Joint, JointDescriptor.bodyFrame0, JointDescriptor.bodyFrame1); 817 | 818 | JointDescriptor.numRows = NumRows; 819 | JointDescriptor.rows = &Rows[CurRows]; 820 | CurRows += NumRows; 821 | } 822 | 823 | immediate::PxCreateJointConstraints(&Header, 1, JointConstraints, ConstraintAllocator, DeltaTime, InvDeltaTime); 824 | } 825 | 826 | bDirtyJointData = false; 827 | 828 | //Contact constraints 829 | const PxU32 NumTotalHeaders = NumContactHeaders + NumJointHeaders; 830 | PxSolverContactDesc ContactDescriptors[4]; 831 | const FContactPair* InnerContactPairs[4]; 832 | const PxU32 NumJoints = JointData.Num(); 833 | 834 | for (PxU32 HeaderIdx = NumJointHeaders; HeaderIdx < NumTotalHeaders; ++HeaderIdx) 835 | { 836 | PxConstraintBatchHeader& Header = BatchHeaders[HeaderIdx]; 837 | check(Header.mConstraintType == PxSolverConstraintDesc::eCONTACT_CONSTRAINT); 838 | 839 | Header.mStartIndex += NumActiveJoints; 840 | 841 | for (PxU32 BatchInnerIdx = 0; BatchInnerIdx < Header.mStride; ++BatchInnerIdx) 842 | { 843 | PxSolverConstraintDesc& OrderedDescriptor = OrderedDescriptors[Header.mStartIndex + BatchInnerIdx]; 844 | PxSolverContactDesc& ContactDescriptor = ContactDescriptors[BatchInnerIdx]; 845 | //Extract the contact pair that we saved in this structure earlier. 846 | const FContactPair& ContactPair = *reinterpret_cast(OrderedDescriptor.constraint); 847 | InnerContactPairs[BatchInnerIdx] = &ContactPair; 848 | 849 | ContactDescriptor.body0 = OrderedDescriptor.bodyA; 850 | ContactDescriptor.body1 = OrderedDescriptor.bodyB; 851 | ContactDescriptor.data0 = &SolverBodiesData[OrderedDescriptor.bodyADataIndex]; 852 | ContactDescriptor.data1 = &SolverBodiesData[OrderedDescriptor.bodyBDataIndex]; 853 | 854 | ContactDescriptor.bodyFrame0 = ContactDescriptor.data0->body2World; 855 | ContactDescriptor.bodyFrame1 = ContactDescriptor.data1->body2World; 856 | 857 | ContactDescriptor.contactForces = nullptr;//&ContactForces[ContactPair.StartContactIndex]; 858 | ContactDescriptor.contacts = &ContactPoints[ContactPair.StartContactIndex]; 859 | ContactDescriptor.numContacts = ContactPair.NumContacts; 860 | 861 | #if PERSISTENT_CONTACT_PAIRS 862 | FPersistentContactPairData& PersistentPairData = ShapeSOA.ContactPairData[ContactPair.PairIdx]; 863 | 864 | ContactDescriptor.frictionPtr = PersistentPairData.Frictions; 865 | ContactDescriptor.frictionCount = PersistentPairData.NumFrictions; 866 | #else 867 | ContactDescriptor.frictionPtr = nullptr; 868 | ContactDescriptor.frictionCount = 0; 869 | #endif 870 | 871 | ContactDescriptor.disableStrongFriction = false; 872 | ContactDescriptor.hasMaxImpulse = false; 873 | ContactDescriptor.hasForceThresholds = false; 874 | ContactDescriptor.shapeInteraction = NULL; 875 | ContactDescriptor.restDistance = 0.f; 876 | ContactDescriptor.maxCCDSeparation = PX_MAX_F32; 877 | 878 | ContactDescriptor.bodyState0 = PxSolverConstraintPrepDescBase::eDYNAMIC_BODY; 879 | ContactDescriptor.bodyState1 = ContactPair.OtherActorDataIndex < NumSimulatedBodies ? PxSolverConstraintPrepDescBase::eDYNAMIC_BODY : PxSolverConstraintPrepDescBase::eSTATIC_BODY; 880 | ContactDescriptor.desc = &OrderedDescriptor; 881 | ContactDescriptor.mInvMassScales.angular0 = ContactDescriptor.mInvMassScales.angular1 = ContactDescriptor.mInvMassScales.linear0 = ContactDescriptor.mInvMassScales.linear1 = 1.f; 882 | } 883 | 884 | immediate::PxCreateContactConstraints(&Header, 1, &ContactDescriptors[0], ConstraintAllocator, InvDeltaTime, -200.f, 4.f, 1.f); 885 | 886 | #if PERSISTENT_CONTACT_PAIRS 887 | // Now cache friction results 888 | for (PxU32 BatchInnerIdx = 0; BatchInnerIdx < Header.mStride; ++BatchInnerIdx) 889 | { 890 | PxSolverContactDesc& ContactDescriptor = ContactDescriptors[BatchInnerIdx]; 891 | 892 | //Extract the contact pair that we saved in this structure earlier. 893 | const FContactPair& ContactPair = *InnerContactPairs[BatchInnerIdx]; 894 | FPersistentContactPairData& PersistentPairData = ShapeSOA.ContactPairData[ContactPair.PairIdx]; 895 | 896 | PersistentPairData.Frictions = ContactDescriptor.frictionPtr; 897 | PersistentPairData.NumFrictions = ContactDescriptor.frictionCount; 898 | } 899 | #endif 900 | } 901 | 902 | #endif 903 | } 904 | 905 | DECLARE_CYCLE_STAT(TEXT("SolveAndIntegrate"), STAT_LocalSolveAndIntegrate, STATGROUP_LocalPhysics); 906 | 907 | 908 | void FLocalSimulation::SolveAndIntegrate(float DeltaTime) 909 | { 910 | #if WITH_PHYSX 911 | SCOPE_CYCLE_COUNTER(STAT_LocalSolveAndIntegrate); 912 | 913 | PxVec3* LinearMotionVelocity = (PxVec3*) Workspace.Alloc(sizeof(PxVec3) * NumActiveSimulatedBodies * 2); 914 | PxVec3* AngularMotionVelocity = &LinearMotionVelocity[NumActiveSimulatedBodies]; 915 | 916 | //Solve all constraints 917 | immediate::PxSolveConstraints(BatchHeaders.GetData(), NumContactHeaders + NumJointHeaders, OrderedDescriptors.GetData(), SolverBodies, LinearMotionVelocity, AngularMotionVelocity, NumActiveSimulatedBodies, NumPositionIterations, NumVelocityIterations); 918 | 919 | //Integrate velocities 920 | immediate::PxIntegrateSolverBodies(SolverBodiesData.GetData(), SolverBodies, LinearMotionVelocity, AngularMotionVelocity, NumActiveSimulatedBodies, DeltaTime); 921 | 922 | //Copy positions of dynamic bodies back into rigid body 923 | for (uint32 DynamicsBodyIdx = 0; DynamicsBodyIdx < NumActiveSimulatedBodies; ++DynamicsBodyIdx) 924 | { 925 | RigidBodiesData[DynamicsBodyIdx].linearVelocity = SolverBodiesData[DynamicsBodyIdx].linearVelocity; 926 | RigidBodiesData[DynamicsBodyIdx].angularVelocity = SolverBodiesData[DynamicsBodyIdx].angularVelocity; 927 | RigidBodiesData[DynamicsBodyIdx].body2World = SolverBodiesData[DynamicsBodyIdx].body2World; 928 | } 929 | #endif 930 | } 931 | 932 | void FLocalSimulation::AddRadialForce(int32 ActorDataIndex, const FVector& InOrigin, float Strength, float Radius, ERadialImpulseFalloff Falloff, EForceType ForceType) 933 | { 934 | #if WITH_PHYSX 935 | if(IsSimulated(ActorDataIndex)) 936 | { 937 | PxVec3 Origin = U2PVector(InOrigin); 938 | immediate::PxRigidBodyData& LowLevelBody = GetLowLevelBody(ActorDataIndex); 939 | const PxTransform& PCOMTransform = LowLevelBody.body2World; 940 | PxVec3 Delta = LowLevelBody.body2World.p - Origin; 941 | 942 | float Mag = Delta.magnitude(); // Distance from COM to origin 943 | if (Mag > Radius) 944 | { 945 | return; 946 | } 947 | 948 | Delta.normalize(); 949 | 950 | // Scale by U2PScale here, because units are velocity * mass. 951 | float ImpulseMag = Strength; 952 | if (Falloff == RIF_Linear) 953 | { 954 | ImpulseMag *= (1.0f - (Mag / Radius)); 955 | } 956 | 957 | const PxVec3 PImpulse = Delta * ImpulseMag; 958 | const PxVec3 ApplyDelta = (ForceType == EForceType::AddAcceleration || ForceType == EForceType::AddVelocity) ? PImpulse : PImpulse * LowLevelBody.invMass; 959 | 960 | if(ForceType == EForceType::AddImpulse || ForceType == EForceType::AddVelocity) 961 | { 962 | LowLevelBody.linearVelocity += ApplyDelta; 963 | } 964 | else 965 | { 966 | PendingAcceleration[ActorDataIndex] += ApplyDelta; 967 | } 968 | } 969 | #endif 970 | } 971 | 972 | 973 | } // namespace LocalPhysics -------------------------------------------------------------------------------- /Plugins/LocalPhysics/Source/LocalPhysics/Private/LocalSimulationVolume.cpp: -------------------------------------------------------------------------------- 1 | // Peter L. Newton - https://twitter.com/peterlnewton 2 | 3 | // local includes for Local Sim Plugin 4 | #include "LocalPhysicsActor.h" 5 | #include "LocalSimulationVolume.h" 6 | #include "LocalPhysicsSimulation.h" 7 | #include "LocalPhysicsActorHandle.h" 8 | #include "LocalPhysicsJointHandle.h" 9 | // includes for global functions 10 | #include "EngineUtils.h" 11 | #include "EngineGlobals.h" 12 | #include "HAL/IConsoleManager.h" 13 | #include "Object.h" 14 | #include "DrawDebugHelpers.h" 15 | #include "Private/KismetTraceUtils.h" 16 | #include "Engine/Engine.h" 17 | #include "SceneManagement.h" 18 | #include "Logging/TokenizedMessage.h" 19 | // includes for components 20 | #include "Components/BoxComponent.h" 21 | #include "Components/MeshComponent.h" 22 | #include "Components/StaticMeshComponent.h" 23 | #include "Components/SceneComponent.h" 24 | #include "Components/PrimitiveComponent.h" 25 | #include "Components/StaticMeshComponent.h" 26 | #include "Components/SkeletalMeshComponent.h" 27 | // includes for assets 28 | #include "Runtime/Engine/Classes/Engine/StaticMesh.h" 29 | // includes for physx 30 | #include "PhysicsEngine/PhysicsSettings.h" 31 | #include "PhysicsEngine/ConstraintInstance.h" 32 | #include "PhysicsEngine/BodySetup.h" 33 | #include "PhysicsPublic.h" 34 | 35 | using namespace LocalPhysics; 36 | 37 | DEFINE_LOG_CATEGORY(LocalSimulationLog); 38 | 39 | /* 40 | * Constructors 41 | */ 42 | 43 | // Sets default values 44 | ALocalSimulationVolume::ALocalSimulationVolume() 45 | { 46 | // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. 47 | // No need to tick, we listen for PhysicsStep updates instead 48 | PrimaryActorTick.bCanEverTick = false; 49 | //PrimaryActorTick.TickGroup = ETickingGroup::TG_PrePhysics; 50 | 51 | delete LocalSpace; 52 | LocalSpace = CreateDefaultSubobject(TEXT("LocalSpace")); 53 | 54 | delete LocalSimulation; 55 | LocalSimulation = new FLocalSimulation(); 56 | } 57 | 58 | ALocalSimulationVolume::~ALocalSimulationVolume() 59 | { 60 | for (LocalPhysicJointData* data : JointActors) 61 | { 62 | auto temp = data; 63 | delete temp; 64 | } 65 | for(LocalPhysicData* data : SimulatedActors) 66 | { 67 | auto temp = data; 68 | LocalSimulation->RemoveActor(temp->InHandle); 69 | temp->InHandle = nullptr; 70 | temp->InPhysicsMesh = nullptr; 71 | temp->InVisualMesh = nullptr; 72 | delete temp; 73 | } 74 | SimulatedActors.Empty(); 75 | JointActors.Empty(); 76 | delete LocalSimulation; 77 | } 78 | 79 | // Called when the game starts or when spawned 80 | void ALocalSimulationVolume::BeginPlay() 81 | { 82 | Super::BeginPlay(); 83 | // register component on begin 84 | LocalSpace->RegisterComponent(); 85 | // get PhysScene, and bind our function to its SceneStep delegate 86 | auto pScene = GetWorld()->GetPhysicsScene(); 87 | OnPhysSceneStepHandle = pScene->OnPhysSceneStep.AddUObject(this, &ALocalSimulationVolume::Update); 88 | } 89 | 90 | void ALocalSimulationVolume::EndPlay(const EEndPlayReason::Type EndPlayReason) 91 | { 92 | // get PhysScene, and remove our function from SceneStep delegate 93 | auto pScene = GetWorld()->GetPhysicsScene(); 94 | if (pScene) 95 | { 96 | pScene->OnPhysSceneStep.Remove(OnPhysSceneStepHandle); 97 | } 98 | } 99 | 100 | /* 101 | * Transform Updates 102 | */ 103 | 104 | void ALocalSimulationVolume::DeferredRemoval() 105 | { 106 | if (bDeferRemovalOfBodies) 107 | { 108 | RemoveJoints(); 109 | RemoveMeshData(); 110 | bDeferRemovalOfBodies = false; 111 | } 112 | //todo: bDeferAdditionOfBodies 113 | } 114 | 115 | void ALocalSimulationVolume::UpdateComponents() 116 | { 117 | /* 118 | * any polling work physics -> real world update 119 | */ 120 | UpdateMeshVisuals(); 121 | //todo: UpdateSkeletalMeshVisuals(); 122 | } 123 | 124 | void ALocalSimulationVolume::SimulatePhysics(float DeltaTime) 125 | { 126 | // todo: update state of dynamic/static objects - i.e. turning on physics or change of mobility will move an static actor to dynamic. 127 | LocalSimulation->Simulate(DeltaTime, LocalRotation.RotateVector(LocalSpace->ComponentToWorld.GetRotation().UnrotateVector(Gravity))); 128 | } 129 | 130 | 131 | void ALocalSimulationVolume::RemoveJoints() 132 | { 133 | for (LocalPhysicJointData* JointData : JointsToRemove) 134 | { 135 | LocalPhysicJointData* temp = JointData; 136 | 137 | LocalSimulation->RemoveJoint(temp->JointHandle); 138 | 139 | temp->Bodies.Empty(); 140 | 141 | temp->JointHandle = nullptr; 142 | 143 | JointBodies--; 144 | 145 | delete temp; 146 | } 147 | JointsToRemove.Empty(); 148 | } 149 | 150 | void ALocalSimulationVolume::RemoveMeshData() 151 | { 152 | for (LocalPhysicData* MeshData : MeshDataToRemove) 153 | { 154 | // create pointers to mesh/handle for use later 155 | LocalPhysicData* temp = MeshData; 156 | 157 | // visual mesh which we want to restore to 158 | UStaticMeshComponent& VisualMesh = *temp->InVisualMesh; 159 | 160 | // prep for reference of handle 161 | LocalPhysics::FActorHandle* Handle = temp->InHandle; 162 | 163 | // create copy of new position in world 164 | const FTransform BodyTransform = Handle->GetWorldTransform() * LocalSpace->ComponentToWorld; 165 | 166 | // store pointer to bodyinstance for later 167 | FBodyInstance* BodyInstance = VisualMesh.GetBodyInstance(); 168 | 169 | // If we are null, no doppel created. let's initialize body back in world 170 | if (temp->InPhysicsMesh == nullptr) 171 | { 172 | BodyInstance->TermBody(); 173 | BodyInstance->InitBody(VisualMesh.GetBodySetup(), BodyTransform, &VisualMesh, GetWorld()->GetPhysicsScene()); 174 | } 175 | 176 | switch (temp->InBodyType) 177 | { 178 | case ELocalPhysicsBodyType::Static: 179 | { 180 | VisualMesh.SetMobility(EComponentMobility::Static); 181 | StaticBodies--; 182 | } 183 | break; 184 | case ELocalPhysicsBodyType::Kinematic: 185 | { 186 | VisualMesh.SetMobility(EComponentMobility::Movable); 187 | KinematicBodies--; 188 | } 189 | break; 190 | case ELocalPhysicsBodyType::Dynamic: 191 | { 192 | // preserve linear / angular velocity for 'local' simulating mesh, and convert it to 'world' space 193 | FVector LinearVelocity = LocalSpace->ComponentToWorld.GetRotation().RotateVector(LocalRotation.UnrotateVector(Handle->GetLinearVelocity())); 194 | FVector AngularVelocity = LocalSpace->ComponentToWorld.GetRotation().RotateVector(LocalRotation.UnrotateVector(Handle->GetAngularVelocity())); 195 | 196 | 197 | VisualMesh.SetMobility(EComponentMobility::Movable); 198 | VisualMesh.SetSimulatePhysics(true); 199 | // restore linear / angular velocity 200 | if (bConvertVelocity) 201 | { 202 | VisualMesh.SetPhysicsLinearVelocity(LinearVelocity); 203 | VisualMesh.SetPhysicsAngularVelocity(AngularVelocity); 204 | } 205 | DynamicBodies--; 206 | } 207 | break; 208 | } 209 | LocalSimulation->RemoveActor(Handle); 210 | SimulatedActors.Remove(MeshData); 211 | delete temp; 212 | } 213 | // no matter what, clear until new request 214 | MeshDataToRemove.Empty(); 215 | } 216 | 217 | void ALocalSimulationVolume::UpdateMeshVisuals() 218 | { 219 | // dynamic/static pass, kinematic is TransformedUpdated below 220 | for (LocalPhysicData* MeshData : SimulatedActors) 221 | { 222 | // dereference pointers to pointers, and set references 223 | UStaticMeshComponent& Mesh = *MeshData->InVisualMesh; 224 | LocalPhysics::FActorHandle& Handle = *MeshData->InHandle; 225 | const FTransform& BodyTransform = Handle.GetWorldTransform() * LocalSpace->GetComponentTransform(); 226 | 227 | switch (MeshData->InBodyType) 228 | { 229 | case ELocalPhysicsBodyType::Static: 230 | case ELocalPhysicsBodyType::Dynamic: 231 | { 232 | // update meshes back in 'world' space 233 | Mesh.SetWorldTransform(BodyTransform, false, nullptr, ETeleportType::TeleportPhysics); 234 | } 235 | break; 236 | case ELocalPhysicsBodyType::Kinematic: 237 | { 238 | // if we are kinematic, we poll updates back into space 239 | Handle.SetWorldTransform(Mesh.ComponentToWorld.GetRelativeTransform(LocalSpace->ComponentToWorld)); 240 | } 241 | break; 242 | } 243 | 244 | // let's show everything in simulation. 245 | if (bShowDebugPhyics) 246 | { 247 | const FTransform& DebugTransform = (bDebugInWorldSpace ? FTransform(BodyTransform.Rotator(), Mesh.Bounds.Origin, BodyTransform.GetScale3D()) : Handle.GetWorldTransform()); 248 | const FVector& DebugExtent = Mesh.GetStaticMesh()->GetBounds().BoxExtent * Handle.ActorScale3D; 249 | UKismetSystemLibrary::DrawDebugBox(GetWorld(), DebugTransform.GetLocation(), DebugExtent, DebugSimulatedColor, DebugTransform.Rotator(), DebugTick, DebugThickness); 250 | } 251 | } 252 | } 253 | 254 | void ALocalSimulationVolume::Update(FPhysScene* PhysScene, uint32 SceneType, float DeltaTime) 255 | { 256 | // only want synchronous tick 257 | if (SceneType == 0) 258 | { 259 | // can't simulate without this 260 | if (LocalSimulation == nullptr) 261 | { 262 | return; 263 | } 264 | // don't simulate if we don't have an Actor Handle 265 | if (LocalSimulation->HandleAvailableToSimulate() == false) 266 | { 267 | return; 268 | } 269 | 270 | // process simulation data 271 | SimulatePhysics(DeltaTime); 272 | 273 | // do any early tick removals 274 | DeferredRemoval(); 275 | 276 | // updates components geoemtry to match rigidbodies 277 | UpdateComponents(); 278 | } 279 | } 280 | 281 | // We listen on Kinematic meshes, as this is called when you SetComponentTransform (and children) 282 | void ALocalSimulationVolume::TransformUpdated(USceneComponent* InRootComponent, EUpdateTransformFlags UpdateTransformFlags, ETeleportType Teleport) const 283 | { 284 | // presumably, this isn't a physics update. - we want to recieve updates from SetComponentTransform 285 | if(Teleport == ETeleportType::None && InRootComponent->Mobility.GetValue() == EComponentMobility::Movable) 286 | { 287 | // Verify this is a Mesh first. 288 | if(InRootComponent->IsA(UStaticMeshComponent::StaticClass())) 289 | { 290 | // We know what it is, but we need access now. 291 | UStaticMeshComponent& Mesh = *Cast(InRootComponent); 292 | 293 | if(LocalPhysicData* MeshData = GetDataForStaticMesh(&Mesh)) 294 | { 295 | // create easy reference for later 296 | LocalPhysics::FActorHandle& Handle = *MeshData->InHandle; 297 | 298 | const FTransform& WorldBodyTransform = Mesh.GetComponentTransform(); 299 | 300 | // Kinematic update for our physics in 'local' space 301 | Handle.SetWorldTransform(WorldBodyTransform.GetRelativeTransform(LocalSpace->ComponentToWorld)); 302 | 303 | // let's show everything in simulation. 304 | if (bShowDebugPhyics) 305 | { 306 | const FTransform& BodyTransform = bDebugInWorldSpace ? WorldBodyTransform : Handle.GetBodyTransform(); 307 | 308 | UKismetSystemLibrary::DrawDebugBox(GetWorld(), BodyTransform.GetLocation(), Mesh.GetStaticMesh()->GetBounds().BoxExtent * Handle.ActorScale3D, DebugKinematicColor, BodyTransform.Rotator(), DebugTick, DebugKinematicThickness); 309 | } 310 | } 311 | } 312 | } 313 | } 314 | 315 | // Helpers 316 | 317 | LocalPhysics::LocalPhysicJointData* ALocalSimulationVolume::GetDataForJoint(UStaticMeshComponent* MeshOne, UStaticMeshComponent* MeshTwo) const 318 | { 319 | LocalPhysics::LocalPhysicData* MeshDataOne = GetDataForStaticMesh(MeshOne); 320 | LocalPhysics::LocalPhysicData* MeshDataTwo = GetDataForStaticMesh(MeshTwo); 321 | 322 | if(MeshDataOne == nullptr && MeshDataTwo == nullptr) 323 | { 324 | return nullptr; 325 | } 326 | 327 | for (LocalPhysicJointData* Joint : JointActors) 328 | { 329 | if (Joint->Bodies.Contains(MeshDataOne) && Joint->Bodies.Contains(MeshDataTwo)) 330 | { 331 | return Joint; 332 | } 333 | } 334 | return nullptr; 335 | } 336 | 337 | LocalPhysicData* ALocalSimulationVolume::GetDataForStaticMesh(UStaticMeshComponent* Mesh) const 338 | { 339 | for (LocalPhysicData* data : SimulatedActors) 340 | { 341 | if (data->InVisualMesh == Mesh) 342 | { 343 | return data; 344 | } 345 | } 346 | return nullptr; 347 | } 348 | 349 | 350 | bool ALocalSimulationVolume::IsInSimulation(UStaticMeshComponent* Mesh) const 351 | { 352 | return GetDataForStaticMesh(Mesh) != nullptr; 353 | } 354 | 355 | FConstraintInstance ALocalSimulationVolume::GetConstraintProfile(int Index) const 356 | { 357 | // 0 is the minimum we will take. 358 | Index = FPlatformMath::Max(0, Index); 359 | 360 | if (Index < ConstraintProfiles.Num()) 361 | { 362 | return ConstraintProfiles[Index]; 363 | } 364 | else 365 | { 366 | return FConstraintInstance(); 367 | } 368 | } 369 | 370 | // addition 371 | // todo: refactor this function 372 | // todo: fall-back setup / error logs when Mesh already exist, etc edge cases 373 | bool ALocalSimulationVolume::AddStaticMeshToSimulation(UStaticMeshComponent* Mesh, bool ShouldExistInBothScenes) 374 | { 375 | bool haveWeAddedMesh = false; 376 | // if we don't find this mesh in Simulated or Kinematic arrays 377 | if (IsInSimulation(Mesh) == false) 378 | { 379 | //UE_LOG(LocalSimulationLog, Warning, L"Current transform: %s is %s", *Mesh->GetStaticMesh()->GetName(), *Mesh->K2_GetComponentScale().ToString()); 380 | /* 381 | * messy check for static, kinematic, dynamic 382 | */ 383 | 384 | FPhysScene* PhysScene = GetWorld()->GetPhysicsScene(); 385 | if (PhysScene == nullptr) 386 | { 387 | return false; 388 | } 389 | 390 | // Scene Lock for Multi-Threading 391 | PxScene* SyncScene = PhysScene->GetPhysXScene(PST_Sync); 392 | SCOPED_SCENE_WRITE_LOCK(SyncScene); //SCOPED_SCENE_WRITE_LOCK or SCOPED_SCENE_READ_LOCK if you only need to read 393 | 394 | // default is Dynamic, other checks will override this default if they're true. 395 | ELocalPhysicsBodyType typeOfAdd = ELocalPhysicsBodyType::Dynamic; 396 | 397 | // check if Kinematic by Component Mobility == Movable && Physics active 398 | typeOfAdd = (Mesh->Mobility.GetValue() == EComponentMobility::Movable) && (Mesh->IsSimulatingPhysics() == false) ? ELocalPhysicsBodyType::Kinematic : typeOfAdd; 399 | 400 | // check if Static by Component Mobility == Static 401 | typeOfAdd = Mesh->Mobility.GetValue() == EComponentMobility::Static ? ELocalPhysicsBodyType::Static : typeOfAdd; 402 | 403 | // init SimulatedActors data 404 | LocalPhysicData* NewMeshData = new LocalPhysicData(*LocalSimulation, Mesh, ShouldExistInBothScenes ? NewObject(this) : nullptr, nullptr, typeOfAdd); 405 | 406 | // create reference to body instance of mesh being added 407 | FBodyInstance& BodyInstance = Mesh->BodyInstance; 408 | 409 | // create copy of new relative transform 410 | const FTransform& BodyTransform = Mesh->GetComponentTransform().GetRelativeTransform(LocalSpace->GetComponentTransform()); 411 | 412 | UStaticMeshComponent* DynamicMesh = NewMeshData->InPhysicsMesh; 413 | 414 | if(ShouldExistInBothScenes) 415 | { 416 | // by default, we create clones for kinematic components. 417 | DynamicMesh->SetMobility(EComponentMobility::Movable); 418 | DynamicMesh->RegisterComponentWithWorld(GetWorld()); 419 | DynamicMesh->SetHiddenInGame(true); 420 | DynamicMesh->SetMobility(EComponentMobility::Movable); 421 | DynamicMesh->SetStaticMesh(Mesh->GetStaticMesh()); 422 | } 423 | else 424 | { 425 | DynamicMesh = NewMeshData->InVisualMesh; 426 | } 427 | 428 | /* always set Visual Mesh movable because this (LocalSimulationVolume) 429 | * actor will move, so even meshes with 'static' mobility will move. 430 | */ 431 | Mesh->SetMobility(EComponentMobility::Movable); 432 | 433 | switch (typeOfAdd) 434 | { 435 | case ELocalPhysicsBodyType::Kinematic: 436 | { 437 | auto kinematicBody = BodyInstance.GetPxRigidBody_AssumesLocked(); 438 | if (kinematicBody == nullptr) 439 | { 440 | return false; 441 | } 442 | // we are going to listen for transform updates from SetComponentTransform (from the original owner) 443 | // I want to say this is still necessary for any updates we get in-between this Actors tick cycle. 444 | Mesh->TransformUpdated.AddUObject(this, &ALocalSimulationVolume::TransformUpdated); 445 | KinematicBodies++; 446 | 447 | NewMeshData->InHandle = LocalSimulation->CreateKinematicActor(kinematicBody, BodyTransform); 448 | } 449 | break; 450 | case ELocalPhysicsBodyType::Static: 451 | { 452 | auto staticBody = BodyInstance.GetPxRigidBody_AssumesLocked(); 453 | if (staticBody == nullptr) 454 | { 455 | return false; 456 | } 457 | // add new mesh into simulation 'local' space 458 | StaticBodies++; 459 | 460 | NewMeshData->InHandle = LocalSimulation->CreateKinematicActor(staticBody, BodyTransform); 461 | } 462 | break; 463 | case ELocalPhysicsBodyType::Dynamic: 464 | { 465 | auto dynamicBody = BodyInstance.GetPxRigidDynamic_AssumesLocked(); 466 | if (dynamicBody == nullptr) 467 | { 468 | return false; 469 | } 470 | DynamicBodies++; 471 | 472 | // preserve linear / angular velocity for 'local' simulating mesh 473 | FVector LinearVelocity = Mesh->GetPhysicsLinearVelocity(); 474 | FVector AngularVelocity = Mesh->GetPhysicsAngularVelocity(); 475 | 476 | DynamicMesh->SetSimulatePhysics(false); 477 | 478 | // create dynamic rigidbody, which is expected to simulate. 479 | NewMeshData->InHandle = LocalSimulation->CreateDynamicActor(dynamicBody, BodyTransform); 480 | 481 | if (bConvertVelocity) 482 | { 483 | NewMeshData->InHandle->SetLinearVelocity(LocalRotation.RotateVector(LocalSpace->ComponentToWorld.GetRotation().UnrotateVector(LinearVelocity))); 484 | NewMeshData->InHandle->SetAngularVelocity(LocalRotation.RotateVector(LocalSpace->ComponentToWorld.GetRotation().UnrotateVector(AngularVelocity))); 485 | } 486 | } 487 | break; 488 | } 489 | 490 | // remove original body from world-space 491 | if(ShouldExistInBothScenes == false) 492 | { 493 | BodyInstance.TermBody(); 494 | } 495 | 496 | // store scale so that Unreal component gets returned the correct scale / debug looks percise. 497 | NewMeshData->InHandle->ActorScale3D = Mesh->K2_GetComponentScale(); 498 | 499 | // create new pair in kinematic meshses array (we don't update on tick) 500 | haveWeAddedMesh = (SimulatedActors.Add(NewMeshData) > -1); 501 | } 502 | return haveWeAddedMesh; 503 | } 504 | 505 | bool ALocalSimulationVolume::AddConstraintToStaticMeshes(UStaticMeshComponent* MeshOne, UStaticMeshComponent* MeshTwo, int ConstraintProfileIndex) 506 | { 507 | 508 | LocalPhysicData* MeshDataOne = GetDataForStaticMesh(MeshOne); 509 | LocalPhysicData* MeshDataTwo = GetDataForStaticMesh(MeshTwo); 510 | 511 | if(MeshDataOne == nullptr || MeshDataTwo == nullptr) 512 | { 513 | UE_LOG(LocalSimulationLog, Error, L"One of the constraint bodies are null."); 514 | return false; 515 | } 516 | 517 | FActorHandle* ActorOne = MeshDataOne->InHandle; 518 | FActorHandle* ActorTwo = MeshDataTwo->InHandle; 519 | 520 | if (ActorOne && ActorTwo) 521 | { 522 | const FConstraintInstance& ConstraintProfile = GetConstraintProfile(ConstraintProfileIndex); 523 | 524 | LocalPhysics::LocalPhysicJointData* newData = new LocalPhysicJointData(*LocalSimulation, { MeshDataOne, MeshDataTwo }, nullptr, { MeshDataOne->InBodyType, MeshDataTwo->InBodyType }); 525 | 526 | PxD6Joint* PD6Joint = PxD6JointCreate(*GPhysXSDK, nullptr, PxTransform(PxIdentity), nullptr, U2PTransform(ActorTwo->GetBodyTransform().GetRelativeTransform(ActorOne->GetBodyTransform()))); 527 | 528 | if(PD6Joint) 529 | { 530 | ConstraintProfile.ProfileInstance.UpdatePhysX_AssumesLocked(PD6Joint, (ActorOne->GetInverseMass() + ActorTwo->GetInverseMass() / 2), 1.f); 531 | 532 | newData->JointHandle = LocalSimulation->CreateJoint(PD6Joint, ActorOne, ActorTwo); 533 | 534 | JointActors.Add(newData); 535 | 536 | return true; 537 | } 538 | else 539 | { 540 | UE_LOG(LocalSimulationLog, Error, L"Failed to create PD6Joint."); 541 | } 542 | } 543 | else 544 | { 545 | UE_LOG(LocalSimulationLog, Error, L"One of the constraint handles are null."); 546 | } 547 | return false; 548 | } 549 | 550 | // removal 551 | 552 | bool ALocalSimulationVolume::RemoveStaticMeshFromSimulation(UStaticMeshComponent* Mesh) 553 | { 554 | if (LocalPhysicData* DataForRemoval = GetDataForStaticMesh(Mesh)) 555 | { 556 | if ( MeshDataToRemove.Contains(DataForRemoval) == false ) 557 | { 558 | MeshDataToRemove.Add(DataForRemoval); 559 | bDeferRemovalOfBodies = true; 560 | return true; 561 | } 562 | else 563 | { 564 | UE_LOG(LocalSimulationLog, Error, L"Already exist: %s is queued for removal.", *Mesh->GetName()); 565 | } 566 | } 567 | else 568 | { 569 | UE_LOG(LocalSimulationLog, Error, L"Could not find: %s in the simulation.", *Mesh->GetName()); 570 | } 571 | return false; 572 | } 573 | 574 | bool ALocalSimulationVolume::RemoveConstraintFromStaticMeshes(UStaticMeshComponent* MeshOne, UStaticMeshComponent* MeshTwo) 575 | { 576 | if( LocalPhysics::LocalPhysicJointData* JointData = GetDataForJoint(MeshOne, MeshTwo) ) 577 | { 578 | JointsToRemove.Add(JointData); 579 | bDeferRemovalOfBodies = true; 580 | } 581 | else 582 | { 583 | UE_LOG(LocalSimulationLog, Error, L"Could not find mesh constraints to remove from the simulation."); 584 | } 585 | return false; 586 | } -------------------------------------------------------------------------------- /Plugins/LocalPhysics/Source/LocalPhysics/Public/ILocalPhysicsPlugin.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2017 Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "LocalPhysicsActor.h" 6 | #include "CoreMinimal.h" 7 | #include "Modules/ModuleInterface.h" 8 | #include "Modules/ModuleManager.h" 9 | 10 | 11 | 12 | /** 13 | * The public interface to this module 14 | */ 15 | class ILocalPhysicsPlugin : public IModuleInterface 16 | { 17 | 18 | public: 19 | 20 | /** 21 | * Singleton-like access to this module's interface. This is just for convenience! 22 | * Beware of calling this during the shutdown phase, though. Your module might have been unloaded already. 23 | * 24 | * @return Returns singleton instance, loading the module on demand if needed 25 | */ 26 | static inline ILocalPhysicsPlugin& Get() 27 | { 28 | return FModuleManager::LoadModuleChecked< ILocalPhysicsPlugin >( "LocalPhysics" ); 29 | } 30 | 31 | /** 32 | * Checks to see if this module is loaded and ready. It is only valid to call Get() if IsAvailable() returns true. 33 | * 34 | * @return True if the module is loaded and ready to use 35 | */ 36 | static inline bool IsAvailable() 37 | { 38 | return FModuleManager::Get().IsModuleLoaded( "LocalPhysics" ); 39 | } 40 | }; 41 | 42 | -------------------------------------------------------------------------------- /Plugins/LocalPhysics/Source/LocalPhysics/Public/LocalPhysicsActor.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2017 Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #if WITH_PHYSX 6 | #include "PhysXPublic.h" 7 | #endif 8 | 9 | #include "LocalPhysicsShape.h" 10 | 11 | namespace LocalPhysics 12 | { 13 | 14 | /** Holds geometry data*/ 15 | struct FActor 16 | { 17 | TArray Shapes; 18 | 19 | #if WITH_PHYSX 20 | /** Create geometry data for the entity */ 21 | void CreateGeometry(PxRigidActor* RigidActor, const PxTransform& ActorToBodyTM); 22 | #endif 23 | 24 | /** Ensures all the geometry data has been properly freed */ 25 | void TerminateGeometry(); 26 | }; 27 | 28 | } -------------------------------------------------------------------------------- /Plugins/LocalPhysics/Source/LocalPhysics/Public/LocalPhysicsActorHandle.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #if WITH_PHYSX 6 | #include "PhysXPublic.h" 7 | #endif 8 | 9 | #include "Engine/EngineTypes.h" 10 | 11 | namespace LocalPhysics 12 | { 13 | 14 | /** handle associated with a physics actor. This is the proper way to read/write to the physics simulation */ 15 | struct LOCALPHYSICS_API FActorHandle 16 | { 17 | 18 | private: 19 | FLocalSimulation& OwningSimulation; 20 | 21 | public: 22 | FVector ActorScale3D; 23 | /** Converts from actor space (i.e. the transform in world space as the client gives us) to body space (body with its origin at the COM and oriented to inertia tensor) */ 24 | FTransform ActorToBody; 25 | int32 ActorDataIndex; 26 | int rigidBodyType = -1; 27 | 28 | /** Sets the world transform.*/ 29 | void SetWorldTransform(const FTransform& WorldTM) 30 | { 31 | #if WITH_PHYSX 32 | OwningSimulation.GetLowLevelBody(ActorDataIndex).body2World = U2PTransform(ActorToBody * WorldTM); 33 | #endif 34 | //ActorScale3D = WorldTM.GetScale3D(); 35 | } 36 | 37 | /** Whether the body is simulating */ 38 | bool IsSimulated() const 39 | { 40 | return OwningSimulation.IsSimulated(ActorDataIndex); 41 | } 42 | 43 | /** Get the world transform */ 44 | FTransform GetWorldTransform() const 45 | { 46 | #if WITH_PHYSX 47 | auto WorldT = ActorToBody.GetRelativeTransformReverse(P2UTransform(OwningSimulation.GetLowLevelBody(ActorDataIndex).body2World)); 48 | WorldT.SetScale3D(ActorScale3D); 49 | return WorldT; 50 | #else 51 | return FTransform::Identity; 52 | #endif 53 | } 54 | 55 | /** Get the world transform */ 56 | FTransform GetBodyTransform() const 57 | { 58 | #if WITH_PHYSX 59 | return FTransform().GetRelativeTransformReverse(P2UTransform(OwningSimulation.GetLowLevelBody(ActorDataIndex).body2World)); 60 | #else 61 | return FTransform::Identity; 62 | #endif 63 | } 64 | 65 | /** Get the world transform */ 66 | FTransform GetProjectedTransform() const 67 | { 68 | #if WITH_PHYSX 69 | auto Transform = ActorToBody; 70 | Transform.SetLocation(FVector::ZeroVector); 71 | Transform = Transform.GetRelativeTransformReverse(P2UTransform(OwningSimulation.GetLowLevelBody(ActorDataIndex).body2World)); 72 | return Transform; 73 | #else 74 | return FTransform::Identity; 75 | #endif 76 | } 77 | 78 | /** Set the linear velocity */ 79 | void SetLinearVelocity(const FVector& NewLinearVelocity) 80 | { 81 | #if WITH_PHYSX 82 | OwningSimulation.GetLowLevelBody(ActorDataIndex).linearVelocity = U2PVector(NewLinearVelocity); 83 | #endif 84 | } 85 | 86 | /** Get the linear velocity */ 87 | FVector GetLinearVelocity() const 88 | { 89 | #if WITH_PHYSX 90 | return P2UVector(OwningSimulation.GetLowLevelBody(ActorDataIndex).linearVelocity); 91 | #else 92 | return FVector::ZeroVector; 93 | #endif 94 | } 95 | 96 | /** Set the angular velocity */ 97 | void SetAngularVelocity(const FVector& NewAngularVelocity) 98 | { 99 | #if WITH_PHYSX 100 | OwningSimulation.GetLowLevelBody(ActorDataIndex).angularVelocity = U2PVector(NewAngularVelocity); 101 | #endif 102 | } 103 | 104 | /** Get the angular velocity */ 105 | FVector GetAngularVelocity() const 106 | { 107 | #if WITH_PHYSX 108 | return P2UVector(OwningSimulation.GetLowLevelBody(ActorDataIndex).angularVelocity); 109 | #else 110 | return FVector::ZeroVector; 111 | #endif 112 | } 113 | 114 | void AddRadialForce(const FVector& Origin, float Strength, float Radius, ERadialImpulseFalloff Falloff, FLocalSimulation::EForceType ForceType) 115 | { 116 | #if WITH_PHYSX 117 | OwningSimulation.AddRadialForce(ActorDataIndex, Origin, Strength, Radius, Falloff, ForceType); 118 | #endif 119 | } 120 | 121 | /** Set the linear damping*/ 122 | void SetLinearDamping(float NewLinearDamping) 123 | { 124 | #if WITH_PHYSX 125 | OwningSimulation.GetLowLevelBody(ActorDataIndex).linearDamping = NewLinearDamping; 126 | #endif 127 | } 128 | 129 | /** Get the linear damping*/ 130 | float GetLinearDamping() const 131 | { 132 | #if WITH_PHYSX 133 | return OwningSimulation.GetLowLevelBody(ActorDataIndex).linearDamping; 134 | #else 135 | return 0.f 136 | #endif 137 | } 138 | 139 | /** Set the angular damping*/ 140 | void SetAngularDamping(float NewAngularDamping) 141 | { 142 | #if WITH_PHYSX 143 | OwningSimulation.GetLowLevelBody(ActorDataIndex).angularDamping = NewAngularDamping; 144 | #endif 145 | } 146 | 147 | /** Get the angular damping*/ 148 | float GetAngularDamping() const 149 | { 150 | #if WITH_PHYSX 151 | return OwningSimulation.GetLowLevelBody(ActorDataIndex).angularDamping; 152 | #else 153 | return 0.f 154 | #endif 155 | } 156 | 157 | /** Set the max linear velocity squared*/ 158 | void SetMaxLinearVelocitySquared(float NewMaxLinearVelocitySquared) 159 | { 160 | #if WITH_PHYSX 161 | OwningSimulation.GetLowLevelBody(ActorDataIndex).maxLinearVelocitySq = NewMaxLinearVelocitySquared; 162 | #endif 163 | } 164 | 165 | /** Get the max linear velocity squared*/ 166 | float GetMaxLinearVelocitySquared() const 167 | { 168 | #if WITH_PHYSX 169 | return OwningSimulation.GetLowLevelBody(ActorDataIndex).maxLinearVelocitySq; 170 | #else 171 | return 0.f 172 | #endif 173 | } 174 | 175 | /** Set the max angular velocity squared*/ 176 | void SetMaxAngularVelocitySquared(float NewMaxAngularVelocitySquared) 177 | { 178 | #if WITH_PHYSX 179 | OwningSimulation.GetLowLevelBody(ActorDataIndex).maxAngularVelocitySq = NewMaxAngularVelocitySquared; 180 | #endif 181 | } 182 | 183 | /** Get the max angular velocity squared*/ 184 | float GetMaxAngularVelocitySquared() const 185 | { 186 | #if WITH_PHYSX 187 | return OwningSimulation.GetLowLevelBody(ActorDataIndex).maxAngularVelocitySq; 188 | #else 189 | return 0.f 190 | #endif 191 | } 192 | 193 | /** Set the inverse mass. 0 indicates kinematic object */ 194 | void SetInverseMass(float NewInverseMass) 195 | { 196 | #if WITH_PHYSX 197 | OwningSimulation.GetLowLevelBody(ActorDataIndex).invMass = NewInverseMass; 198 | #endif 199 | } 200 | 201 | /** Get the inverse mass. */ 202 | float GetInverseMass() const 203 | { 204 | #if WITH_PHYSX 205 | return OwningSimulation.GetLowLevelBody(ActorDataIndex).invMass; 206 | #else 207 | return 0.f 208 | #endif 209 | } 210 | 211 | /** Set the inverse inertia. Mass-space inverse inertia diagonal vector */ 212 | void SetInverseInertia(const FVector& NewInverseInertia) 213 | { 214 | #if WITH_PHYSX 215 | OwningSimulation.GetLowLevelBody(ActorDataIndex).invInertia = U2PVector(NewInverseInertia); 216 | #endif 217 | } 218 | 219 | /** Get the inverse inertia. Mass-space inverse inertia diagonal vector */ 220 | FVector GetInverseInertia() const 221 | { 222 | #if WITH_PHYSX 223 | return P2UVector(OwningSimulation.GetLowLevelBody(ActorDataIndex).invInertia); 224 | #else 225 | return 0.f 226 | #endif 227 | } 228 | 229 | /** Set the max depenetration velocity*/ 230 | void SetMaxDepenetrationVelocity(float NewMaxDepenetrationVelocity) 231 | { 232 | #if WITH_PHYSX 233 | OwningSimulation.GetLowLevelBody(ActorDataIndex).maxDepenetrationVelocity = NewMaxDepenetrationVelocity; 234 | #endif 235 | } 236 | 237 | /** Get the max depenetration velocity*/ 238 | float GetMaxDepenetrationVelocity(float NewMaxDepenetrationVelocity) const 239 | { 240 | #if WITH_PHYSX 241 | return OwningSimulation.GetLowLevelBody(ActorDataIndex).maxDepenetrationVelocity; 242 | #else 243 | return 0.f 244 | #endif 245 | } 246 | 247 | /** Set the max contact impulse*/ 248 | void SetMaxContactImpulse(float NewMaxContactImpulse) 249 | { 250 | #if WITH_PHYSX 251 | OwningSimulation.GetLowLevelBody(ActorDataIndex).maxContactImpulse = NewMaxContactImpulse; 252 | #endif 253 | } 254 | 255 | /** Get the max contact impulse*/ 256 | float GetMaxContactImpulse() const 257 | { 258 | #if WITH_PHYSX 259 | return OwningSimulation.GetLowLevelBody(ActorDataIndex).maxContactImpulse; 260 | #else 261 | return 0.f 262 | #endif 263 | } 264 | 265 | friend FLocalSimulation; 266 | FActorHandle(FLocalSimulation& InOwningSimulation, int32 InActorDataIndex) 267 | : ActorToBody(FTransform::Identity) 268 | , OwningSimulation(InOwningSimulation) 269 | , ActorDataIndex(InActorDataIndex) 270 | { 271 | } 272 | 273 | ~FActorHandle() 274 | { 275 | } 276 | 277 | FActorHandle(const FActorHandle& ); //Ensure no copying of handles 278 | }; 279 | 280 | 281 | } -------------------------------------------------------------------------------- /Plugins/LocalPhysics/Source/LocalPhysics/Public/LocalPhysicsCacheAllocator.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #if WITH_PHYSX 6 | #include "PhysXPublic.h" 7 | #endif 8 | 9 | #include "LocalPhysicsLinearBlockAllocator.h" 10 | 11 | namespace LocalPhysics 12 | { 13 | 14 | #if WITH_PHYSX 15 | /** TODO: Use a smarter memory allocator */ 16 | struct FCacheAllocator : public PxCacheAllocator 17 | { 18 | FCacheAllocator() : External(0){} 19 | PxU8* allocateCacheData(const PxU32 ByteSize) override 20 | { 21 | return BlockAllocator[External].Alloc(ByteSize); 22 | } 23 | 24 | void Reset() 25 | { 26 | #if PERSISTENT_CONTACT_PAIRS 27 | External = 1 - External; //flip buffer so we maintain cache for 1 extra step 28 | #endif 29 | BlockAllocator[External].Reset(); 30 | } 31 | 32 | FLinearBlockAllocator BlockAllocator[2]; 33 | int32 External; 34 | }; 35 | #endif 36 | 37 | } -------------------------------------------------------------------------------- /Plugins/LocalPhysics/Source/LocalPhysics/Public/LocalPhysicsConstraintAllocator.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #if WITH_PHYSX 6 | #include "PhysXPublic.h" 7 | #endif 8 | 9 | #include "LocalPhysicsLinearBlockAllocator.h" 10 | 11 | namespace LocalPhysics 12 | { 13 | 14 | #if WITH_PHYSX 15 | /** TODO: Use a smarter memory allocator */ 16 | class FConstraintAllocator : public PxConstraintAllocator 17 | { 18 | public: 19 | FConstraintAllocator() : External(0){} 20 | 21 | PxU8* reserveConstraintData(const PxU32 ByteSize) override 22 | { 23 | return BlockAllocator[External].Alloc(ByteSize); 24 | 25 | } 26 | 27 | PxU8* reserveFrictionData(const PxU32 ByteSize) 28 | { 29 | return BlockAllocator[External].Alloc(ByteSize); 30 | } 31 | 32 | void Reset() 33 | { 34 | #if PERSISTENT_CONTACT_PAIRS 35 | External = 1 - External; //flip buffer so we maintain cache for 1 extra step 36 | #endif 37 | BlockAllocator[External].Reset(); 38 | } 39 | 40 | FLinearBlockAllocator BlockAllocator[2]; 41 | int32 External; 42 | }; 43 | #endif 44 | 45 | } -------------------------------------------------------------------------------- /Plugins/LocalPhysics/Source/LocalPhysics/Public/LocalPhysicsContactPair.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2017 Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | namespace LocalPhysics 6 | { 7 | 8 | /** Contact pair generated between entities */ 9 | struct FContactPair 10 | { 11 | /** Index of the dynamic actor that we generated the contact pair for*/ 12 | int32 DynamicActorDataIndex; 13 | 14 | /** Index of the other actor that we generated the contact pair for. This could be either dynamic or static */ 15 | uint32 OtherActorDataIndex; 16 | 17 | /** Index into the first contact point associated with this pair*/ 18 | uint32 StartContactIndex; 19 | 20 | /** Number of contacts associated with this pair. */ 21 | uint32 NumContacts; 22 | 23 | /** Identifies the pair index from the original contact generation test */ 24 | int32 PairIdx; 25 | }; 26 | 27 | 28 | } -------------------------------------------------------------------------------- /Plugins/LocalPhysics/Source/LocalPhysics/Public/LocalPhysicsContactPointRecorder.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2017 Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "LocalPhysicsActor.h" 6 | #if WITH_PHYSX 7 | #include "PhysXPublic.h" 8 | #endif 9 | 10 | 11 | namespace LocalPhysics 12 | { 13 | struct FLocalSimulation; 14 | 15 | #if WITH_PHYSX 16 | struct FContactPointRecorder : public immediate::PxContactRecorder 17 | { 18 | FContactPointRecorder(struct FLocalSimulation& InSimulation, int32 InDynamicActorDataIndex, int32 InOtherActorDataIndex, int32 InPairIdx) 19 | : Simulation(InSimulation) 20 | , DynamicActorDataIndex(InDynamicActorDataIndex) 21 | , OtherActorDataIndex(InOtherActorDataIndex) 22 | , PairIdx(InPairIdx) 23 | { 24 | } 25 | 26 | bool recordContacts(const Gu::ContactPoint* ContactPoints, const PxU32 NumContacts, const PxU32 Index) override; 27 | 28 | FLocalSimulation& Simulation; 29 | int32 DynamicActorDataIndex; 30 | int32 OtherActorDataIndex; 31 | int32 PairIdx; 32 | }; 33 | #endif 34 | 35 | } -------------------------------------------------------------------------------- /Plugins/LocalPhysics/Source/LocalPhysics/Public/LocalPhysicsD6JointData.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "LocalPhysicsActor.h" 6 | #if WITH_PHYSX 7 | #include "PhysXPublic.h" 8 | #endif 9 | 10 | 11 | 12 | //TODO: this is just straight copied from physx 13 | namespace LocalPhysics 14 | { 15 | 16 | #if WITH_PHYSX 17 | struct D6JointData 18 | { 19 | D6JointData(PxD6Joint* Joint); 20 | 21 | /** End solver API */ 22 | 23 | 24 | PxConstraintInvMassScale invMassScale; 25 | PxTransform c2b[2]; 26 | 27 | PxU32 locked; // bitmap of locked DOFs 28 | PxU32 limited; // bitmap of limited DOFs 29 | PxU32 driving; // bitmap of active drives (implies driven DOFs not locked) 30 | 31 | 32 | PxD6Motion::Enum motion[6]; 33 | PxJointLinearLimit linearLimit; 34 | PxJointAngularLimitPair twistLimit; 35 | PxJointLimitCone swingLimit; 36 | 37 | PxD6JointDrive drive[PxD6Drive::eCOUNT]; 38 | 39 | PxTransform drivePosition; 40 | PxVec3 driveLinearVelocity; 41 | PxVec3 driveAngularVelocity; 42 | 43 | // derived quantities 44 | 45 | 46 | // tan-half and tan-quarter angles 47 | 48 | PxReal thSwingY; 49 | PxReal thSwingZ; 50 | PxReal thSwingPad; 51 | 52 | PxReal tqSwingY; 53 | PxReal tqSwingZ; 54 | PxReal tqSwingPad; 55 | 56 | PxReal tqTwistLow; 57 | PxReal tqTwistHigh; 58 | PxReal tqTwistPad; 59 | 60 | PxReal linearMinDist; // linear limit minimum distance to get a good direction 61 | 62 | // projection quantities 63 | //PxReal projectionLinearTolerance; 64 | //PxReal projectionAngularTolerance; 65 | 66 | FTransform ActorToBody[2]; 67 | 68 | bool HasConstraints() const 69 | { 70 | return locked || limited || driving; 71 | } 72 | }; 73 | 74 | void PrepareJointData(D6JointData& JointData); 75 | PxU32 D6JointSolverPrep(Px1DConstraint* constraints, PxVec3& body0WorldOffset, PxU32 maxConstraints, PxConstraintInvMassScale& invMassScale, const void* constantBlock, const PxTransform& bA2w, const PxTransform& bB2w); 76 | 77 | } 78 | #endif -------------------------------------------------------------------------------- /Plugins/LocalPhysics/Source/LocalPhysics/Public/LocalPhysicsJoint.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | namespace LocalPhysics 6 | { 7 | 8 | struct FActorHandle; 9 | 10 | struct FJoint 11 | { 12 | FActorHandle* DynamicActor; 13 | FActorHandle* OtherActor; 14 | }; 15 | 16 | } -------------------------------------------------------------------------------- /Plugins/LocalPhysics/Source/LocalPhysics/Public/LocalPhysicsJointHandle.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #if WITH_PHYSX 6 | #include "PhysXPublic.h" 7 | #endif 8 | 9 | namespace LocalPhysics 10 | { 11 | struct FLocalSimulation; 12 | 13 | /** handle associated with a physics joint. This is the proper way to read/write to the physics simulation */ 14 | struct LOCALPHYSICS_API FJointHandle 15 | { 16 | private: 17 | FLocalSimulation& OwningSimulation; 18 | int32 JointDataIndex; 19 | 20 | friend FLocalSimulation; 21 | FJointHandle(FLocalSimulation& InOwningSimulation, int32 InJointDataIndex) 22 | : OwningSimulation(InOwningSimulation) 23 | , JointDataIndex(InJointDataIndex) 24 | { 25 | } 26 | 27 | ~FJointHandle() 28 | { 29 | } 30 | 31 | FJointHandle(const FJointHandle&); //Ensure no copying of handles 32 | 33 | }; 34 | 35 | } -------------------------------------------------------------------------------- /Plugins/LocalPhysics/Source/LocalPhysics/Public/LocalPhysicsLinearBlockAllocator.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | namespace LocalPhysics 6 | { 7 | const int PageBufferSize = 1024*64; //This has to be here because of VS2013 8 | 9 | struct FLinearBlockAllocator 10 | { 11 | struct FPageStruct 12 | { 13 | //We assume FPageStruct is allocated with 16-byte alignment. Do not move Buffer. If we get full support for alignas(16) we could make this better 14 | uint8 Buffer[PageBufferSize]; 15 | 16 | FPageStruct* NextPage; 17 | FPageStruct* PrevPage; 18 | int32 SeekPosition; 19 | 20 | FPageStruct() 21 | : NextPage(nullptr) 22 | , PrevPage(nullptr) 23 | , SeekPosition(0) 24 | { 25 | } 26 | }; 27 | 28 | FPageStruct* FreePage; 29 | FPageStruct* FirstPage; 30 | 31 | FLinearBlockAllocator() 32 | { 33 | FreePage = AllocPage(); 34 | FirstPage = FreePage; 35 | } 36 | 37 | FPageStruct* AllocPage() 38 | { 39 | FPageStruct* ReturnPage = (FPageStruct*)FMemory::Malloc(sizeof(FPageStruct), 16); 40 | new(ReturnPage) FPageStruct(); 41 | 42 | FPlatformMisc::TagBuffer("LocalPhysicsSim", 0, (const void*)ReturnPage, sizeof(FPageStruct)); 43 | return ReturnPage; 44 | } 45 | 46 | void ReleasePage(FPageStruct* Page) 47 | { 48 | FMemory::Free(Page); 49 | } 50 | 51 | uint8* Alloc(int32 Bytes) 52 | { 53 | check(Bytes < PageBufferSize); //Page size needs to be increased since we don't allow spillover 54 | if (Bytes) 55 | { 56 | //Assumes 16 byte alignment 57 | int32 BytesLeft = PageBufferSize - FreePage->SeekPosition; //don't switch to uint because negative implies we're out of 16 byte aligned space 58 | if (BytesLeft < Bytes) 59 | { 60 | //no space so allocate new page if needed 61 | if (FreePage->NextPage) 62 | { 63 | FreePage = FreePage->NextPage; 64 | } 65 | else 66 | { 67 | FPageStruct* NewPage = AllocPage(); 68 | NewPage->PrevPage = FreePage; 69 | FreePage->NextPage = NewPage; 70 | FreePage = NewPage; 71 | } 72 | } 73 | 74 | uint32 ReturnSlot = FreePage->SeekPosition; 75 | FreePage->SeekPosition = (FreePage->SeekPosition + Bytes + 15)&(~15); 76 | 77 | return &FreePage->Buffer[ReturnSlot]; 78 | } 79 | else 80 | { 81 | return nullptr; 82 | } 83 | } 84 | 85 | void Reset() 86 | { 87 | for (FPageStruct* Page = FirstPage; Page; Page = Page->NextPage) 88 | { 89 | Page->SeekPosition = 0; 90 | } 91 | 92 | FreePage = FirstPage; 93 | } 94 | 95 | void Empty() 96 | { 97 | FPageStruct* CurrentPage = FirstPage->NextPage; 98 | while (CurrentPage) 99 | { 100 | FPageStruct* OldPage = CurrentPage; 101 | CurrentPage = CurrentPage->NextPage; 102 | ReleasePage(OldPage); 103 | } 104 | 105 | FirstPage->NextPage = nullptr; 106 | FirstPage->SeekPosition = 0; 107 | FreePage = FirstPage; 108 | //Note we do not deallocate FirstPage until the allocator deallocates 109 | } 110 | 111 | ~FLinearBlockAllocator() 112 | { 113 | Empty(); 114 | ReleasePage(FirstPage); 115 | } 116 | 117 | private: 118 | //Don't copy these around 119 | FLinearBlockAllocator(const FLinearBlockAllocator& Other); 120 | const FLinearBlockAllocator& operator=(const FLinearBlockAllocator& Other); 121 | }; 122 | 123 | } -------------------------------------------------------------------------------- /Plugins/LocalPhysics/Source/LocalPhysics/Public/LocalPhysicsModule.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "ModuleInterface.h" 6 | 7 | /** 8 | * ImediatePhysics Edit mode module interface 9 | */ 10 | class IImediatePhysicsModule : public IModuleInterface 11 | { 12 | public: 13 | }; 14 | -------------------------------------------------------------------------------- /Plugins/LocalPhysics/Source/LocalPhysics/Public/LocalPhysicsPersistentContactPairData.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #ifndef PERSISTENT_CONTACT_PAIRS 6 | #define PERSISTENT_CONTACT_PAIRS 1 7 | #endif 8 | 9 | namespace LocalPhysics 10 | { 11 | 12 | #if PERSISTENT_CONTACT_PAIRS 13 | /** Persistent data like contact manifold, friction patches, etc... */ 14 | struct FPersistentContactPairData 15 | { 16 | #if WITH_PHYSX 17 | PxCache Cache; 18 | PxU8* Frictions; 19 | PxU32 NumFrictions; 20 | #endif 21 | 22 | uint32 SimCount; 23 | 24 | void Clear() 25 | { 26 | FPlatformMemory::Memzero(this, sizeof(FPersistentContactPairData)); 27 | } 28 | }; 29 | #endif 30 | 31 | 32 | } -------------------------------------------------------------------------------- /Plugins/LocalPhysics/Source/LocalPhysics/Public/LocalPhysicsShape.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2017 Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #if WITH_PHYSX 6 | #include "PhysXPublic.h" 7 | #endif 8 | 9 | namespace LocalPhysics 10 | { 11 | /** Holds shape data*/ 12 | struct FShape 13 | { 14 | #if WITH_PHYSX 15 | const PxTransform LocalTM; 16 | const PxGeometry* Geometry; 17 | const PxVec3 BoundsOffset; 18 | const float BoundsMagnitude; 19 | 20 | FShape(const PxTransform& InLocalTM, const PxVec3& InBoundsOffset, const float InBoundsMagnitude, const PxGeometry* InGeometry) 21 | : LocalTM(InLocalTM) 22 | , Geometry(InGeometry) 23 | , BoundsOffset(InBoundsOffset) 24 | , BoundsMagnitude(InBoundsMagnitude) 25 | { 26 | } 27 | #endif 28 | }; 29 | 30 | } -------------------------------------------------------------------------------- /Plugins/LocalPhysics/Source/LocalPhysics/Public/LocalPhysicsSimulation.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2017 Epic Games, Inc. All Rights Reserved. 2 | // Peter L. Newton - https://twitter.com/peterlnewton 3 | 4 | #pragma once 5 | 6 | #include "LocalPhysicsActor.h" 7 | #if WITH_PHYSX 8 | #include "PhysXPublic.h" 9 | #include "LocalPhysicsD6JointData.h" 10 | #endif 11 | 12 | #include "LocalPhysicsActor.h" 13 | #include "LocalPhysicsJoint.h" 14 | #include "LocalPhysicsLinearBlockAllocator.h" 15 | #include "LocalPhysicsContactPair.h" 16 | #include "LocalPhysicsPersistentContactPairData.h" 17 | #include "LocalPhysicsCacheAllocator.h" 18 | #include "LocalPhysicsConstraintAllocator.h" 19 | 20 | 21 | DECLARE_STATS_GROUP(TEXT("Local Physics"), STATGROUP_LocalPhysics, STATCAT_Advanced); 22 | 23 | class UBodySetup; 24 | class UPhysicsConstraintTemplate; 25 | class UStaticMeshComponent; 26 | 27 | namespace LocalPhysics 28 | { 29 | struct FActorHandle; 30 | struct FJointHandle; 31 | } 32 | 33 | namespace LocalPhysics 34 | { 35 | /** Owns all the data associated with the simulation. Can be considered a single scene or world*/ 36 | struct LOCALPHYSICS_API FLocalSimulation 37 | { 38 | bool HandleAvailableToSimulate() const; 39 | public: 40 | 41 | #if WITH_PHYSX 42 | /** Create a kinematic body and add it to the simulation */ 43 | FActorHandle* CreateKinematicActor(PxRigidBody* RigidBody, const FTransform& TM); 44 | 45 | /** Create a dynamic body and add it to the simulation */ 46 | FActorHandle* CreateDynamicActor(PxRigidDynamic* RigidDynamic, const FTransform& TM); 47 | 48 | /** Create a static body and add it to the simulation */ 49 | FActorHandle* CreateStaticActor(PxRigidActor* RigidActor, const FTransform& TM); 50 | 51 | /** Create a physical joint and add it to the simulation */ 52 | FJointHandle* CreateJoint(PxD6Joint* Joint, FActorHandle* Body1, FActorHandle* Body2); 53 | 54 | void RemoveActor(FActorHandle* Handle); 55 | void RemoveJoint(FJointHandle* Handle); 56 | #endif 57 | 58 | 59 | 60 | /** Sets the number of active bodies. This number is reset any time a new simulated body is created */ 61 | void SetNumActiveBodies(uint32 NumActiveBodies); 62 | 63 | /** An array of actors to ignore. */ 64 | struct FIgnorePair 65 | { 66 | FActorHandle* A; 67 | FActorHandle* B; 68 | }; 69 | 70 | /** Set pair of bodies to ignore collision for */ 71 | void SetIgnoreCollisionPairTable(const TArray& InIgnoreCollisionPairTable); 72 | 73 | /** Set bodies that require no collision */ 74 | void SetIgnoreCollisionActors(const TArray& InIgnoreCollision); 75 | 76 | /** Advance the simulation by DeltaTime */ 77 | void Simulate(float DeltaTime, const FVector& Gravity); 78 | 79 | /** Whether or not an entity is simulated */ 80 | bool IsSimulated(uint32 ActorDataIndex) const 81 | { 82 | return ActorDataIndex < NumSimulatedBodies; 83 | } 84 | 85 | enum class EForceType 86 | { 87 | AddForce, //use mass and delta time 88 | AddAcceleration, //use delta time, ignore mass 89 | AddImpulse, //use mass, ignore delta time 90 | AddVelocity //ignore mass, ignore delta time 91 | }; 92 | 93 | /** Add a radial impulse to the given actor */ 94 | void AddRadialForce(int32 ActorDataIndex, const FVector& Origin, float Strength, float Radius, ERadialImpulseFalloff Falloff, EForceType ForceType); 95 | 96 | FLocalSimulation(); 97 | 98 | ~FLocalSimulation(); 99 | 100 | private: 101 | friend FActorHandle; 102 | 103 | #if WITH_PHYSX 104 | const immediate::PxRigidBodyData& GetLowLevelBody(int32 ActorDataIndex) const 105 | { 106 | return RigidBodiesData[ActorDataIndex]; 107 | } 108 | 109 | immediate::PxRigidBodyData& GetLowLevelBody(int32 ActorDataIndex) 110 | { 111 | return RigidBodiesData[ActorDataIndex]; 112 | } 113 | #endif 114 | 115 | enum class ECreateActorType 116 | { 117 | StaticActor, //collision but no movement 118 | KinematicActor, //collision + movement but no dynamics (forces, mass, etc...) 119 | DynamicActor //collision + movement + dynamics 120 | }; 121 | 122 | #if WITH_PHYSX 123 | template 124 | uint32 CreateActor(PxRigidActor* RigidActor, const FTransform& TM); 125 | #endif 126 | 127 | /** Swap actor data - that is move all data associated with the two actors in the various arrays*/ 128 | void SwapActorData(uint32 Entity1Idx, uint32 Entity2Idx); 129 | 130 | /** Swap joint data - that is move all data associated with the two joints in the various arrays*/ 131 | void SwapJointData(uint32 Joint1Idx, uint32 Joint2Idx); 132 | 133 | /** Ensure arrays are valid */ 134 | void ValidateArrays() const; 135 | 136 | /** Constructs solver bodies */ 137 | void ConstructSolverBodies(float DeltaTime, const FVector& Gravity); 138 | 139 | /** Generate contacts*/ 140 | void GenerateContacts(); 141 | 142 | /** Batch constraints and re-order them for optimal processing */ 143 | void BatchConstraints(); 144 | 145 | /** Prepares the various constraints (contact,joints) for the solver */ 146 | void PrepareConstraints(float DeltaTime); 147 | 148 | /** Solve constraints and integrate velocities */ 149 | void SolveAndIntegrate(float DeltaTime); 150 | 151 | /** Prepares iteration cache for generating contacts */ 152 | void PrepareIterationCache(); 153 | 154 | //void EvictCache(); 155 | 156 | private: 157 | 158 | /** Mapping from entity index to handle */ 159 | TArray ActorHandles; 160 | 161 | /** Mapping from constraint index to handle */ 162 | TArray JointHandles; 163 | 164 | /** Entities holding loose data. NOTE: for performance reasons we don't automatically cleanup on destructor (needed for tarray swaps etc...) it's very important that Terminate is called */ 165 | TArray Actors; 166 | 167 | TArray Joints; 168 | 169 | /** Workspace memory that we use for per frame allocations */ 170 | FLinearBlockAllocator Workspace; 171 | 172 | #if WITH_PHYSX 173 | /** Low level rigid body data */ 174 | TArray RigidBodiesData; 175 | 176 | /** Low level solver bodies data */ 177 | TArray SolverBodiesData; 178 | 179 | TArray PendingAcceleration; 180 | 181 | /** Low level contact points generated for this frame. Points are grouped together by pairs */ 182 | TArray ContactPoints; 183 | 184 | /** Shapes used in the entire simulation. Shapes are sorted in the same order as actors. Note that an actor can have multiple shapes which will be adjacent*/ 185 | struct FShapeSOA 186 | { 187 | TArray LocalTMs; 188 | TArray Geometries; 189 | TArray Bounds; 190 | TArray BoundsOffsets; 191 | TArray OwningActors; 192 | #if PERSISTENT_CONTACT_PAIRS 193 | TArray ContactPairData; 194 | #endif 195 | } ShapeSOA; 196 | 197 | /** Low level solver bodies */ 198 | PxSolverBody* SolverBodies; 199 | 200 | /** Low level constraint descriptors.*/ 201 | TArray OrderedDescriptors; 202 | TArray BatchHeaders; 203 | 204 | /** JointData as passed in from physics constraint template */ 205 | TArray JointData; 206 | 207 | /** When new joints are created we have to update the processing order */ 208 | bool bDirtyJointData; 209 | 210 | PxU32 NumContactHeaders; 211 | PxU32 NumJointHeaders; 212 | uint32 NumActiveJoints; 213 | #endif 214 | 215 | /** Contact pairs generated for this frame */ 216 | TArray ContactPairs; 217 | 218 | /** Number of dynamic bodies associated with the simulation */ 219 | uint32 NumSimulatedBodies; 220 | 221 | /** Number of dynamic bodies that are actually active */ 222 | uint32 NumActiveSimulatedBodies; 223 | 224 | /** Number of kinematic bodies (dynamic but not simulated) associated with the simulation */ 225 | uint32 NumKinematicBodies; 226 | 227 | /** Total number of simulated shapes in the scene */ 228 | uint32 NumSimulatedShapesWithCollision; 229 | 230 | /** Number of position iterations used by solver */ 231 | uint32 NumPositionIterations; 232 | 233 | /** Number of velocity iterations used by solver */ 234 | uint32 NumVelocityIterations; 235 | 236 | /** Count of how many times we've ticked. Useful for cache invalidation */ 237 | uint32 SimCount; 238 | 239 | /** Both of these are slow to access. Make sure to use iteration cache when possible */ 240 | TMap> IgnoreCollisionPairTable; 241 | TSet IgnoreCollisionActors; 242 | 243 | /** This cache is used to record which generate contact iteration we can skip. This assumes the iteration order has not changed (add/remove/swap actors must invalidate this) */ 244 | bool bRecreateIterationCache; 245 | 246 | TArray SkipCollisionCache; //Holds the iteration count that we should skip due to ignore filtering 247 | 248 | friend struct FContactPointRecorder; 249 | 250 | FCacheAllocator CacheAllocator; 251 | FConstraintAllocator ConstraintAllocator; 252 | }; 253 | 254 | } -------------------------------------------------------------------------------- /Plugins/LocalPhysics/Source/LocalPhysics/Public/LocalSimulationVolume.h: -------------------------------------------------------------------------------- 1 | // Peter L. Newton - https://twitter.com/peterlnewton 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "GameFramework/Actor.h" 7 | #include "PhysicsEngine/ConstraintInstance.h" 8 | #include "LocalSimulationVolume.generated.h" 9 | 10 | class UBoxComponent; 11 | class UMeshComponent; 12 | class UStaticMeshComponent; 13 | 14 | namespace LocalPhysics 15 | { 16 | struct FLocalSimulation; 17 | struct FActorHandle; 18 | struct FJointHandle; 19 | 20 | enum class ELocalPhysicsBodyType 21 | { 22 | None = 0x00, 23 | Static = 0x01, 24 | Kinematic = 0x02, 25 | Dynamic = 0x04 26 | }; 27 | // This structure is used to abstract data within the LocalSimulation for a Static/(todo: SkeletalMeshComponent) 28 | struct LOCALPHYSICS_API LocalPhysicData 29 | { 30 | LocalPhysics::FLocalSimulation& OwningSimulation; 31 | UStaticMeshComponent* InVisualMesh; 32 | UStaticMeshComponent* InPhysicsMesh; 33 | LocalPhysics::FActorHandle* InHandle; 34 | ELocalPhysicsBodyType InBodyType; 35 | LocalPhysicData(LocalPhysics::FLocalSimulation& InSimulation, UStaticMeshComponent* Visual, UStaticMeshComponent* Physics, LocalPhysics::FActorHandle* Handle, ELocalPhysicsBodyType BodyType) 36 | : OwningSimulation(InSimulation), InVisualMesh(Visual), InPhysicsMesh(Physics), InHandle(Handle), InBodyType(BodyType) 37 | {} 38 | }; 39 | // This structure is used to abstract data within the LocalSimulation for Joints 40 | struct LOCALPHYSICS_API LocalPhysicJointData 41 | { 42 | LocalPhysics::FLocalSimulation& OwningSimulation; 43 | TArray Bodies; 44 | LocalPhysics::FJointHandle* JointHandle; 45 | TArray Types; 46 | ELocalPhysicsBodyType BodyTypeOne; 47 | ELocalPhysicsBodyType BodyTypeTwo; 48 | LocalPhysicJointData(LocalPhysics::FLocalSimulation& InSimulation, TArray NewBodies, LocalPhysics::FJointHandle* Joint, TArray Types) 49 | : OwningSimulation(InSimulation), Bodies(NewBodies), JointHandle(Joint), Types(Types) 50 | {} 51 | }; 52 | } 53 | 54 | DECLARE_LOG_CATEGORY_EXTERN(LocalSimulationLog, Log, All) 55 | 56 | UCLASS(Blueprintable) 57 | class LOCALPHYSICS_API ALocalSimulationVolume : public AActor 58 | { 59 | GENERATED_BODY() 60 | public: 61 | // Sets default values for this actor's properties 62 | ALocalSimulationVolume(); 63 | ~ALocalSimulationVolume(); 64 | 65 | // Acts as a editor widget to visualize rotation. 66 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Local Simulation") 67 | UBoxComponent* LocalSpace; 68 | 69 | // Tracking Static Bodies 70 | UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Local Simulation") 71 | int StaticBodies = 0; 72 | 73 | // Tracking Kinematic Bodies 74 | UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Local Simulation") 75 | int KinematicBodies = 0; 76 | 77 | // Tracking Dynamic Bodies 78 | UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Local Simulation") 79 | int DynamicBodies = 0; 80 | 81 | // Tracking Joint Handles 82 | UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Local Simulation") 83 | int JointBodies = 0; 84 | 85 | // Override 'local' space gravity 86 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Local Simulation") 87 | FVector Gravity = FVector(0.f, 0.f, -980.f); 88 | 89 | // Offset 'local' space rotation 90 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Local Simulation") 91 | FRotator LocalRotation = FRotator::ZeroRotator; 92 | 93 | // Show volumes which represnt objects in 'local' space 94 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Local Simulation") 95 | bool bShowDebugPhyics = false; 96 | 97 | // Force debug to show in world space rather than local 98 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Local Simulation") 99 | bool bDebugInWorldSpace = false; 100 | 101 | // Should the velocity from the previous space be converted to the new space. (Affects Adding & Removing) 102 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Local Simulation") 103 | bool bConvertVelocity = true; 104 | 105 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Local Simulation") 106 | FColor DebugSimulatedColor = FColor::Red; 107 | 108 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Local Simulation") 109 | FColor DebugKinematicColor = FColor::Green; 110 | 111 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Local Simulation") 112 | float DebugThickness = 1.f; 113 | 114 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Local Simulation") 115 | float DebugKinematicThickness = 5.f; 116 | 117 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Local Simulation") 118 | float DebugTick = 1.f; 119 | 120 | // Define constraints to be used with `RemoveConstraintFromStaticMeshes` 121 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Local Simulation") 122 | TArray ConstraintProfiles; 123 | protected: 124 | 125 | TArray SimulatedActors; 126 | TArray JointActors; 127 | 128 | private: 129 | FDelegateHandle OnPhysSceneStepHandle; 130 | 131 | LocalPhysics::FLocalSimulation* LocalSimulation; 132 | 133 | TArray MeshDataToRemove; 134 | TArray JointsToRemove; 135 | bool bDeferRemovalOfBodies = false; 136 | 137 | protected: 138 | // Called when the game starts or when spawned 139 | virtual void BeginPlay() override; 140 | // Use this to clean up before the actor PhysScene is destroyed 141 | virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; 142 | 143 | private: 144 | void DeferredRemoval(); 145 | void UpdateComponents(); 146 | void SimulatePhysics(float DeltaTime); 147 | 148 | void RemoveJoints(); 149 | void RemoveMeshData(); 150 | 151 | void UpdateMeshVisuals(); 152 | 153 | // Use to simulate along with PhysScene 154 | void Update(FPhysScene* PhysScene, uint32 SceneType, float DeltaTime); 155 | 156 | // Used to update Kinematic actors within Local Simulation 157 | void TransformUpdated(USceneComponent* InRootComponent, EUpdateTransformFlags UpdateTransformFlags, ETeleportType Teleport) const; 158 | 159 | public: 160 | LocalPhysics::LocalPhysicData* GetDataForStaticMesh(UStaticMeshComponent* Mesh) const; 161 | LocalPhysics::LocalPhysicJointData* GetDataForJoint(UStaticMeshComponent* MeshOne, UStaticMeshComponent* MeshTwo) const; 162 | 163 | // Check whether this mesh is associated with this space. 164 | UFUNCTION(BlueprintCallable, Category = "Local Simulation") 165 | bool IsInSimulation(UStaticMeshComponent* Mesh) const; 166 | 167 | // Break constraints associated with these mesh. 168 | UFUNCTION(BlueprintCallable, Category = "Local Simulation") 169 | FConstraintInstance GetConstraintProfile(int Index) const; 170 | 171 | // Add mesh to this space. 172 | UFUNCTION(BlueprintCallable, Category = "Local Simulation") 173 | bool AddStaticMeshToSimulation(UStaticMeshComponent* Mesh, bool ShouldExistInBothScenes); 174 | 175 | // Check whether this mesh is associated with this space. 176 | UFUNCTION(BlueprintCallable, Category = "Local Simulation") 177 | bool AddConstraintToStaticMeshes(UStaticMeshComponent* MeshOne, UStaticMeshComponent* MeshTwo, int ConstraintProfileIndex); 178 | 179 | // Remove mesh from this space. 180 | UFUNCTION(BlueprintCallable, Category = "Local Simulation") 181 | bool RemoveStaticMeshFromSimulation(UStaticMeshComponent* Mesh); 182 | 183 | // Break constraints associated with these mesh. 184 | UFUNCTION(BlueprintCallable, Category = "Local Simulation") 185 | bool RemoveConstraintFromStaticMeshes(UStaticMeshComponent* MeshOne, UStaticMeshComponent* MeshTwo); 186 | }; 187 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [LocalSimulation](https://peterlnewton.com) 2 | 3 | *Abstraction of Immediate Physx API used to create low cost Physics Simulations within Unreal Engine 4.* 4 | 5 | This was made possible thanks to the work by 4.16 from UE4 Devs new Immediate Physics. 6 | https://www.unrealengine.com/en-US/blog/gdc-2017-making-physics-based-mayhem 7 | 8 | Wouldn't be here without their work exposing PhysX API. 9 | 10 | I've taken it a step further, and abtracted the implementation using a Blueprint Actor. Now you can control low level physics with just a few commands in Blueprint. :) 11 | 12 | ## Version 13 | 4.17.2 (Planned) 14 | 15 | 4.16.2 (Currently) 16 | 17 | ## Why 18 | This was made to allow you to easily ignore world forces for physics simulations i.e. playing catch on a train. Imagine now you can play catch, and the simulation would respond in a way that the world isn't moving at all. Allowing for predictable results in environment that require this behavior. 19 | 20 | ## LocalSimulation 21 | This plugin allows you to essentially create PxScene or Physic Scenes by placing an Actor called Local Simulation Volume, and adding other Static Mesh Components and soon Skeletal Mesh components within this Actor. Adding Constraints, and Forces will work as expected, but now with the additional layer of abstraction so that world simulation can be associated with a transform. 22 | 23 | ## TODO 24 | 25 | # Skeletal Meshes 26 | 7/16/2017 27 | >Solution ready for implementation. Transform issue was making joints impossible, which make Skeletal Meshes worthless. 28 | >Next push will contain first pass implementation of Skeletal Mesh Components. 29 | >After that, I will slowly abstract the Blueprint commands further to support Mesh Components, to make use of this Actor much easier. 30 | 6/31/2017 31 | >Tested plan yet to implement, but wanting a better solution. 32 | >>Create bodies of Skeletal Mesh, then copy Skeletal Mesh to UPoseableMeshComponent. Map bodies to new 33 | >>UPoseableMeshComponent and blend Animation from original Skeletal Mesh per bone as determiend by flag 34 | >>which says its 'Default/Kinematic/Simualted'. 35 | 36 | # Transform / Scale 37 | 7/16/2017 38 | >Appears this is because there is a ActorToBody offset that wasn't adjusted for. This has been affecting all aspects of Physics, but it is now rememedied. 39 | >Scale issue continues to percist. 40 | 41 | 6/31/2017 42 | >Appeared to get weird effects when scaling of components, but unable to decide exactly what is influencing it. Currently 43 | >I believe it has to do with Static vs (Kinematic / Dynamic?). Still not 100% sure but really sketchy behavior which needs 44 | >to be made clear. 45 | 46 | # Removing Joints / Acors 47 | 7/16/2017 48 | >Everything has been solved. Unreal has a flipped frame of reference, and on top of the transform offset from the body. It wasn't clear what was happening with the joints. Now that is solved, next push will include polished integration of joints. 49 | 6/31/2017 50 | >This works for the most part, but also needs to be reviewed. 51 | 52 | # Adding Forces to Bodies / Simulation space. 53 | 7/16/2017 54 | >R&D done. This will be imeplemented after the completely adaption of Sketal & Static rigidbodies. As abstracting the two components will make operations which they both will inevitability need, easier to use. 55 | 6/31/2017 56 | >Need to make this possible. 57 | 58 | # Updating Joints / Actors 59 | 7/16/2017 60 | >This problem is now apparrent now that more things work as expected. That is happening is that in-between Physic Simuations aka where Contacts occur. Removing & Adding joints can introduce large amount of forces. 61 | >It would also be nice to have the ability to change from static, kinematic, and dynamic at runtime. Given this is an underlying proxy, this "should just work". 62 | 63 | # Etc functions I'm not thinking of that are necessary. -------------------------------------------------------------------------------- /Source/LocalSimulation.Target.cs: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | using UnrealBuildTool; 4 | using System.Collections.Generic; 5 | 6 | public class LocalSimulationTarget : TargetRules 7 | { 8 | public LocalSimulationTarget(TargetInfo Target) : base(Target) 9 | { 10 | Type = TargetType.Game; 11 | 12 | ExtraModuleNames.AddRange( new string[] { "LocalSimulation" } ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Source/LocalSimulation/LocalSimulation.Build.cs: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class LocalSimulation : ModuleRules 6 | { 7 | public LocalSimulation(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; 10 | 11 | PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" }); 12 | 13 | PrivateDependencyModuleNames.AddRange(new string[] { }); 14 | 15 | // Uncomment if you are using Slate UI 16 | // PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" }); 17 | 18 | // Uncomment if you are using online features 19 | // PrivateDependencyModuleNames.Add("OnlineSubsystem"); 20 | 21 | // To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Source/LocalSimulation/LocalSimulation.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #include "LocalSimulation.h" 4 | #include "Modules/ModuleManager.h" 5 | 6 | IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, LocalSimulation, "LocalSimulation" ); 7 | -------------------------------------------------------------------------------- /Source/LocalSimulation/LocalSimulation.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | 7 | -------------------------------------------------------------------------------- /Source/LocalSimulation/LocalSimulationGameModeBase.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #include "LocalSimulationGameModeBase.h" 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Source/LocalSimulation/LocalSimulationGameModeBase.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "GameFramework/GameModeBase.h" 7 | #include "LocalSimulationGameModeBase.generated.h" 8 | 9 | /** 10 | * 11 | */ 12 | UCLASS() 13 | class LOCALSIMULATION_API ALocalSimulationGameModeBase : public AGameModeBase 14 | { 15 | GENERATED_BODY() 16 | 17 | 18 | 19 | 20 | }; 21 | -------------------------------------------------------------------------------- /Source/LocalSimulationEditor.Target.cs: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | using UnrealBuildTool; 4 | using System.Collections.Generic; 5 | 6 | public class LocalSimulationEditorTarget : TargetRules 7 | { 8 | public LocalSimulationEditorTarget(TargetInfo Target) : base(Target) 9 | { 10 | Type = TargetType.Editor; 11 | 12 | ExtraModuleNames.AddRange( new string[] { "LocalSimulation" } ); 13 | } 14 | } 15 | --------------------------------------------------------------------------------