private static void onContactModify(PxContactModifyCallback * @this, PxContactModifyPair *pairs, uint count) { for (uint i = 0; i < count; i++) { //BIOQUIRK: Probably the "more correct" thing to do here is to use the generated constant array type, but these types currently have long confusing names. // A Span<PxRigidActor*> would also be nice, but isn't possible in C# today. // Could be improved by https://github.com/MochiLibraries/Biohazrd/issues/139 PxRigidActor **actors = (PxRigidActor **)&pairs[i].actor; //BIOQUIRK: Unobvious cast. PxShape ** shapes = (PxShape **)&pairs[i].shape; //BIOQUIRK: Unobvious cast //Search for actors that represent vehicles and shapes that represent wheels. for (int j = 0; j < 2; j++) { PxRigidActor *actor = actors[j]; if (actor->userData != null && ((ActorUserData *)(actor->userData))->vehicle != null) { PxVehicleWheels *vehicle = ((ActorUserData *)(actor->userData))->vehicle; Debug.Assert(vehicle->getRigidDynamicActor() == actors[j]); PxShape *shape = shapes[j]; if (shape->userData != null && ((ShapeUserData *)(shape->userData))->isWheel) { uint wheelId = ((ShapeUserData *)(shape->userData))->wheelId; Debug.Assert(wheelId < vehicle->mWheelsSimData.getNbWheels()); //Modify wheel contacts. PxVehicleModifyWheelContacts(*vehicle, wheelId, WHEEL_TANGENT_VELOCITY_MULTIPLIER, MAX_IMPULSE, ref pairs[i]); } } } } }
private static void onCCDContactModify(PxCCDContactModifyCallback * @this, PxContactModifyPair *pairs, uint count) { for (uint i = 0; i < count; i++) { PxRigidActor **actors = (PxRigidActor **)&pairs[i].actor; //BIOQUIRK: Unobvious cast. PxShape ** shapes = (PxShape **)&pairs[i].shape; //BIOQUIRK: Unobvious cast //Search for actors that represent vehicles and shapes that represent wheels. for (uint j = 0; j < 2; j++) { PxRigidActor *actor = actors[j]; if (actor->userData != null && ((ActorUserData *)(actor->userData))->vehicle != null) { PxVehicleWheels *vehicle = ((ActorUserData *)(actor->userData))->vehicle; Debug.Assert(vehicle->getRigidDynamicActor() == actors[j]); PxShape *shape = shapes[j]; if (shape->userData != null && ((ShapeUserData *)(shape->userData))->isWheel) { uint wheelId = ((ShapeUserData *)(shape->userData))->wheelId; Debug.Assert(wheelId < vehicle->mWheelsSimData.getNbWheels()); //Modify wheel contacts. PxVehicleModifyWheelContacts(*vehicle, wheelId, WHEEL_TANGENT_VELOCITY_MULTIPLIER, MAX_IMPULSE, ref pairs[i]); } } } } }
static void createKinematics() { const uint NbX = NB_KINE_X; const uint NbY = NB_KINE_Y; PxVec3 dims = new(1.5f, 0.2f, 1.5f); PxQuat rot = new PxQuat(PxIdentity); const float YScale = 0.4f; PxShape *shape = gPhysics->createShape(new PxBoxGeometry(dims), *gMaterial, false, PxShapeFlags.eVISUALIZATION | PxShapeFlags.eSCENE_QUERY_SHAPE | PxShapeFlags.eSIMULATION_SHAPE); //BIOQUIRK: Missing default arguments const float ScaleX = KINE_SCALE; const float ScaleY = KINE_SCALE; for (uint y = 0; y < NbY; y++) { for (uint x = 0; x < NbX; x++) { float xf = ((float)(x) - (float)(NbX) * 0.5f) * ScaleX; float yf = ((float)(y) - (float)(NbY) * 0.5f) * ScaleY; PxTransform pose = new(new PxVec3(xf, 0.2f + YScale, yf), rot); PxRigidDynamic *body = gPhysics->createRigidDynamic(pose); body->attachShape(ref *shape); gScene->addActor(ref *body); body->setRigidBodyFlag(PxRigidBodyFlags.eKINEMATIC, true); gKinematics[y, x] = body; } } }
/** * Creates two example collections: * - collection with actors and joints that can be instantiated multiple times in the scene * - collection with shared objects */ static void createCollections(ref PxCollection *sharedCollection, ref PxCollection *actorCollection, ref PxSerializationRegistry sr) { PxMaterial *material = gPhysics->createMaterial(0.5f, 0.5f, 0.6f); float halfLength = 2.0f, height = 25.0f; PxVec3 offset = new(halfLength, 0, 0); PxRigidActor *prevActor = (PxRigidActor *)PxCreateStatic(ref *gPhysics, new PxTransform(new PxVec3(0, height, 0)), new PxSphereGeometry(halfLength), ref *material, new PxTransform(offset)); //BIOQUIRK: Base cast PxShape *shape = gPhysics->createShape(new PxBoxGeometry(halfLength, 1.0f, 1.0f), *material, false, PxShapeFlags.eVISUALIZATION | PxShapeFlags.eSCENE_QUERY_SHAPE | PxShapeFlags.eSIMULATION_SHAPE); //BIOQUIRK: Missing defaults for (uint i = 1; i < 8; i++) { PxTransform tm = new(new PxVec3((float)(i * 2) * halfLength, height, 0)); PxRigidDynamic *dynamic = gPhysics->createRigidDynamic(tm); dynamic->attachShape(ref *shape); PxRigidBodyExt.updateMassAndInertia(ref *dynamic, 10.0f); PxSphericalJointCreate(ref *gPhysics, prevActor, new PxTransform(offset), dynamic, new PxTransform(offset.operator_Minus())); //BIOQUIRK: Operator overload prevActor = (PxRigidActor *)dynamic; //BIOQUIRK: Base cast } sharedCollection = PxCreateCollection(); // collection for all the shared objects actorCollection = PxCreateCollection(); // collection for all the nonshared objects sharedCollection->add(ref *shape); PxSerialization.complete(ref *sharedCollection, ref sr); // chases the pointer from shape to material, and adds it PxSerialization.createSerialObjectIds(ref *sharedCollection, 77); // arbitrary choice of base for references to shared objects actorCollection->add(ref *prevActor); PxSerialization.complete(ref *actorCollection, ref sr, sharedCollection, true); // chases all pointers and recursively adds actors and joints }
static void createGroudPlane() { PxTransform pose = new PxTransform(new PxVec3(0.0f, 0.0f, 0.0f), new PxQuat(MathF.PI * 0.5f, new PxVec3(0.0f, 0.0f, 1.0f))); PxRigidStatic *actor = gPhysics->createRigidStatic(pose); PxShape * shape = PxRigidActorExt.createExclusiveShape(ref *actor, new PxPlaneGeometry(), *gMaterial, PxShapeFlags.eVISUALIZATION | PxShapeFlags.eSCENE_QUERY_SHAPE | PxShapeFlags.eSIMULATION_SHAPE); //BIOQUIRK: Missing default gScene->addActor(ref *actor); }
private static PxFilterFlags *pairFound(PxSimulationFilterCallback * @this, PxFilterFlags *_retBuf, uint pairID, uint attributes0, PxFilterData *filterData0, PxActor *a0, PxShape *s0, uint attributes1, PxFilterData *filterData1, PxActor *a1, PxShape *s1, PxPairFlags *pairFlags) { // Console.WriteLine("pairFound"); if (s0->userData != null || s1->userData != null) // See createTriggerShape() function { *pairFlags = PxPairFlags.eTRIGGER_DEFAULT; if (usesCCD()) { *pairFlags |= PxPairFlags.eDETECT_CCD_CONTACT | PxPairFlags.eNOTIFY_TOUCH_CCD; } } else { *pairFlags = PxPairFlags.eCONTACT_DEFAULT; } *_retBuf = default; return(_retBuf); }
static void createDynamics() { const uint NbX = 8; const uint NbY = 8; PxVec3 dims = new(0.2f, 0.1f, 0.2f); const float sphereRadius = 0.2f; const float capsuleRadius = 0.2f; const float halfHeight = 0.5f; const uint NbLayers = 3; const float YScale = 0.4f; const float YStart = 6.0f; PxShape * boxShape = gPhysics->createShape(new PxBoxGeometry(dims), *gMaterial, false, PxShapeFlags.eVISUALIZATION | PxShapeFlags.eSCENE_QUERY_SHAPE | PxShapeFlags.eSIMULATION_SHAPE); //BIOQUIRK: Missing default arguments PxShape *sphereShape = gPhysics->createShape(new PxSphereGeometry(sphereRadius), *gMaterial, false, PxShapeFlags.eVISUALIZATION | PxShapeFlags.eSCENE_QUERY_SHAPE | PxShapeFlags.eSIMULATION_SHAPE); //BIOQUIRK: Missing default arguments PxShape *capsuleShape = gPhysics->createShape(new PxCapsuleGeometry(capsuleRadius, halfHeight), *gMaterial, false, PxShapeFlags.eVISUALIZATION | PxShapeFlags.eSCENE_QUERY_SHAPE | PxShapeFlags.eSIMULATION_SHAPE); //BIOQUIRK: Missing default arguments PxMat33 m; for (uint j = 0; j < NbLayers; j++) { float angle = (float)j * 0.08f; PxQuat rot = setRotY(out m, angle); const float ScaleX = 4.0f; const float ScaleY = 4.0f; for (uint y = 0; y < NbY; y++) { for (uint x = 0; x < NbX; x++) { float xf = ((float)(x) - (float)(NbX) * 0.5f) * ScaleX; float yf = ((float)(y) - (float)(NbY) * 0.5f) * ScaleY; PxRigidDynamic *dynamic = null; uint v = j & 3; PxVec3 pos = new PxVec3(xf, YStart + (float)(j) * YScale, yf); switch (v) { case 0: { PxTransform pose = new(pos, rot); dynamic = gPhysics->createRigidDynamic(pose); dynamic->attachShape(ref *boxShape); break; } case 1: { PxTransform pose = new(pos, new PxQuat(PxIdentity)); dynamic = gPhysics->createRigidDynamic(pose); dynamic->attachShape(ref *sphereShape); break; } default: { PxTransform pose = new(pos, rot); dynamic = gPhysics->createRigidDynamic(pose); dynamic->attachShape(ref *capsuleShape); break; } } ; PxRigidBodyExt.updateMassAndInertia(ref *dynamic, 10f); gScene->addActor(ref *dynamic); } } } }
public static void Main(string[] args) { Console.WriteLine($"PhysX native runtime build information: '{MochiPhysX.BuildInfo}'..."); //--------------------------------------------------------------------------------------------------------------------------------------- Console.WriteLine("Initializing error callback"); // Switch between these to use PhysX's default error callback or one implemented from C# PxDefaultErrorCallback errorCallback = new(); //PxErrorCallback errorCallback = ErrorCallback.Create(); //--------------------------------------------------------------------------------------------------------------------------------------- Console.WriteLine("Initializing allocator callback"); // Switch between these to use PhysX's default allocator callback or one implemented from C# PxDefaultAllocator allocator = new(); //PxAllocatorCallback allocator = BasicAllocator.Create(); //PxAllocatorCallback allocator = LoggingAllocator.Create(); //--------------------------------------------------------------------------------------------------------------------------------------- Console.WriteLine("Initializing foundation"); //BIOQUIRK: PhysX owns both of these references, which means both the allocator and error callback must remain pinned for the lifetime of the foundation. // (In our case they're stack allocated and implicitly pinned.) // This seems somewhat unobvious since C# references don't normally care. Should we emit this function differently to convey the unsafe-ness here? PxFoundation *foundation = PxCreateFoundation(PX_PHYSICS_VERSION, ref allocator, ref errorCallback); if (foundation == null) { Console.Error.WriteLine("Failed to create foundation."); return; } //--------------------------------------------------------------------------------------------------------------------------------------- Console.WriteLine("Initializing Pvd..."); //BIOQUIRK: This pattern comes up a lot in PhysX. It does technically match how it is in C++ though, not sure if it's a problem. // I experimentally enabled having C++ reference returns translate as C# reference returns, but that creates a weird situation when you need to store them. PxPvd *pvd = PxCreatePvd(ref *foundation); PxPvdTransport *transport; byte[] host = Encoding.ASCII.GetBytes("127.0.0.1"); fixed(byte *hostP = host) { transport = PxDefaultPvdSocketTransportCreate(hostP, 5425, 10); } Console.WriteLine("Connecting to Pvd..."); pvd->connect(ref *transport, PxPvdInstrumentationFlags.eALL); //--------------------------------------------------------------------------------------------------------------------------------------- Console.WriteLine("Initializing physics"); PxPhysics *physics = PxCreatePhysics(PX_PHYSICS_VERSION, ref *foundation, new PxTolerancesScale(), trackOutstandingAllocations: true, pvd); if (physics == null) { Console.Error.WriteLine("Failed to create physics."); return; } //--------------------------------------------------------------------------------------------------------------------------------------- Console.WriteLine("Creating dispatcher"); PxDefaultCpuDispatcher *dispatcher = PxDefaultCpuDispatcherCreate(2, null); //--------------------------------------------------------------------------------------------------------------------------------------- Console.WriteLine("Creating scene"); PxSceneDesc sceneDescription = new(*physics->getTolerancesScale()); sceneDescription.gravity = new PxVec3() { x = 0f, y = -9.81f, z = 0f }; sceneDescription.cpuDispatcher = (PxCpuDispatcher *)dispatcher; sceneDescription.filterShader = PxDefaultSimulationFilterShader; PxScene *scene = physics->createScene(sceneDescription); //--------------------------------------------------------------------------------------------------------------------------------------- Console.WriteLine("Configuring scene Pvd client"); PxPvdSceneClient *pvdClient = scene->getScenePvdClient(); if (pvdClient != null) { pvdClient->setScenePvdFlag(PxPvdSceneFlags.eTRANSMIT_CONSTRAINTS, true); pvdClient->setScenePvdFlag(PxPvdSceneFlags.eTRANSMIT_CONTACTS, true); pvdClient->setScenePvdFlag(PxPvdSceneFlags.eTRANSMIT_SCENEQUERIES, true); } //--------------------------------------------------------------------------------------------------------------------------------------- Console.WriteLine("Creating a basic material"); PxMaterial *material = physics->createMaterial(0.5f, 0.5f, 0.6f); //--------------------------------------------------------------------------------------------------------------------------------------- Console.WriteLine("Adding a ground plane"); PxPlane planeDescription = new PxPlane() { n = new PxVec3() { x = 0f, y = 1f, z = 0f }, d = 0f }; PxRigidStatic *groundPlane = PxCreatePlane(ref *physics, planeDescription, ref *material); scene->addActor(ref *groundPlane, null); //--------------------------------------------------------------------------------------------------------------------------------------- Console.WriteLine("Adding stacks"); { const float halfExtent = 2f; PxBoxGeometry stackBoxGeometry = new PxBoxGeometry(halfExtent, halfExtent, halfExtent); PxShapeFlags shapeFlags = PxShapeFlags.eVISUALIZATION | PxShapeFlags.eSCENE_QUERY_SHAPE | PxShapeFlags.eSIMULATION_SHAPE; //BIOQUIRK: shapeFlags should be able to be defaulted now but it isn't. PxShape *shape = physics->createShape(stackBoxGeometry, *material, isExclusive: false, shapeFlags); float stackZ = 10f; for (int stackNum = 0; stackNum < 5; stackNum++) { const int size = 10; PxVec3 transformPosition = new PxVec3() { x = 0f, y = 0f, z = stackZ -= 10f }; PxTransform transform = new(transformPosition); for (int i = 0; i < size; i++) { for (int j = 0; j < size - i; j++) { PxVec3 position = new PxVec3() { x = ((float)(j * 2) - (float)(size - i)) * halfExtent, y = ((float)(i * 2 + 1)) * halfExtent, z = 0f }; PxTransform localTransform = new(position); PxTransform bodyTransform = transform.transform(localTransform); PxRigidDynamic *body = physics->createRigidDynamic(bodyTransform); body->attachShape(ref *shape); PxRigidBodyExt.updateMassAndInertia(ref *body, 10f); scene->addActor(ref *body); } } } shape->release(); } //--------------------------------------------------------------------------------------------------------------------------------------- Console.WriteLine("Throwing a ball at the stacks"); { PxVec3 position = new PxVec3() { x = 0f, y = 40f, z = 100f, }; PxTransform transform = new(position); PxSphereGeometry geometry = new(10f); PxVec3 velocity = new PxVec3() { x = 0f, y = -50f, z = -100f }; PxTransform identity = new(default(PxIDENTITY)); //BIOQUIRK: This could be a special generated property instead. Also missing default. PxRigidDynamic *dynamic = PxCreateDynamic(ref *physics, transform, geometry, ref *material, 10f, identity); dynamic->setAngularDamping(0.5f); dynamic->setLinearVelocity(velocity); scene->addActor(ref *dynamic); } //--------------------------------------------------------------------------------------------------------------------------------------- const int noInputFrameCount = 100; Console.WriteLine($"Simulating the world{(Console.IsInputRedirected ? $" for {noInputFrameCount} frames." : "... (Press escape to stop.)")}"); Stopwatch sw = new Stopwatch(); int frameNum = 0; const uint scratchMemoryBlockSize = 16 * 1024; const uint scratchMemoryBlockCount = 4; uint scratchMemorySize = scratchMemoryBlockSize * scratchMemoryBlockCount; void * scratchMemory = allocator.allocate(scratchMemorySize, null, null, 0); while (true) { double msSinceLastTick = sw.Elapsed.TotalMilliseconds; string consoleTitle = $"Simulating frame {frameNum} -- {msSinceLastTick:0.00} ms -- {1.0 / (msSinceLastTick / 1000.0):00.0} FPS"; if (BasicAllocator.AllocationCount > 0) // This is only applicable when a allocator implemented in C# is in use, assume 0 allocations implies the PhysX one is being used { consoleTitle += $" -- {BasicAllocator.AllocationCount} allocations"; BasicAllocator.AllocationCount = 0; } Console.Title = consoleTitle; frameNum++; sw.Restart(); scene->simulate(1f / 60f, scratchMemBlock: scratchMemory, scratchMemBlockSize: scratchMemorySize); uint errors; scene->fetchResults(true, &errors); if (errors != 0) { Console.WriteLine($"fetchResults error: {errors}"); } if (Console.IsInputRedirected) { if (frameNum > noInputFrameCount) { break; } } else if (Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Escape) { break; } } //--------------------------------------------------------------------------------------------------------------------------------------- Console.WriteLine("Shutting down"); allocator.deallocate(scratchMemory); physics->release(); foundation->release(); }
static void createLongChain() { const float scale = 0.25f; const float radius = 0.5f * scale; const float halfHeight = 1.0f * scale; const uint nbCapsules = 40; const float capsuleMass = 1.0f; PxVec3 initPos = new(0.0f, 24.0f, 0.0f); PxVec3 pos = initPos; PxShape *capsuleShape = gPhysics->createShape(new PxCapsuleGeometry(radius, halfHeight), *gMaterial, false, PxShapeFlags.eVISUALIZATION | PxShapeFlags.eSCENE_QUERY_SHAPE | PxShapeFlags.eSIMULATION_SHAPE); //BIOQUIRK: Missing defaults PxArticulationLink *firstLink = null; PxArticulationLink *parent = null; const bool overlappingLinks = true; // Change this for another kind of rope gArticulation->setSolverIterationCounts(16); // Create rope for (uint i = 0; i < nbCapsules; i++) { PxArticulationLink *link = gArticulation->createLink(parent, new PxTransform(pos)); if (firstLink == null) { firstLink = link; } link->attachShape(ref *capsuleShape); PxRigidBodyExt.setMassAndUpdateInertia(ref *link, capsuleMass); link->setLinearDamping(0.1f); link->setAngularDamping(0.1f); link->setMaxAngularVelocity(30f); link->setMaxLinearVelocity(100f); PxArticulationJointBase *joint = link->getInboundJoint(); if (joint != null) // Will be null for root link { #if USE_REDUCED_COORDINATE_ARTICULATION PxArticulationJointReducedCoordinate *rcJoint = static_cast <PxArticulationJointReducedCoordinate>(joint); rcJoint->setJointType(PxArticulationJointType.eSPHERICAL); rcJoint->setMotion(PxArticulationAxis.eSWING2, PxArticulationMotions.eFREE); rcJoint->setMotion(PxArticulationAxis.eSWING1, PxArticulationMotions.eFREE); rcJoint->setMotion(PxArticulationAxis.eTWIST, PxArticulationMotions.eFREE); rcJoint->setFrictionCoefficient(1f); rcJoint->setMaxJointVelocity(1000000f); #endif if (overlappingLinks) { joint->setParentPose(new PxTransform(new PxVec3(halfHeight, 0.0f, 0.0f))); joint->setChildPose(new PxTransform(new PxVec3(-halfHeight, 0.0f, 0.0f))); } else { joint->setParentPose(new PxTransform(new PxVec3(radius + halfHeight, 0.0f, 0.0f))); joint->setChildPose(new PxTransform(new PxVec3(-radius - halfHeight, 0.0f, 0.0f))); } } if (overlappingLinks) { pos.x += (radius + halfHeight * 2.0f); } else { pos.x += (radius + halfHeight) * 2.0f; } parent = link; } //Attach large & heavy box at the end of the rope { const float boxMass = 50.0f; const float boxSize = 1.0f; PxShape * boxShape = gPhysics->createShape(new PxBoxGeometry(boxSize, boxSize, boxSize), *gMaterial, false, PxShapeFlags.eVISUALIZATION | PxShapeFlags.eSCENE_QUERY_SHAPE | PxShapeFlags.eSIMULATION_SHAPE); //BIOQUIRK: Missing defaults pos.x -= (radius + halfHeight) * 2.0f; pos.x += (radius + halfHeight) + boxSize; PxArticulationLink *link = gArticulation->createLink(parent, new PxTransform(pos)); link->setLinearDamping(0.1f); link->setAngularDamping(0.1f); link->setMaxAngularVelocity(30f); link->setMaxLinearVelocity(100f); link->attachShape(ref *boxShape); PxRigidBodyExt.setMassAndUpdateInertia(ref *link, boxMass); PxArticulationJointBase *joint = link->getInboundJoint(); #if USE_REDUCED_COORDINATE_ARTICULATION PxArticulationJointReducedCoordinate *rcJoint = static_cast <PxArticulationJointReducedCoordinate>(joint); rcJoint->setJointType(PxArticulationJointType.eSPHERICAL); rcJoint->setMotion(PxArticulationAxis.eSWING2, PxArticulationMotions.eFREE); rcJoint->setMotion(PxArticulationAxis.eSWING1, PxArticulationMotions.eFREE); rcJoint->setMotion(PxArticulationAxis.eTWIST, PxArticulationMotions.eFREE); rcJoint->setFrictionCoefficient(1f); rcJoint->setMaxJointVelocity(1000000f); #endif if (joint != null) // Will be null for root link { joint->setParentPose(new PxTransform(new PxVec3(radius + halfHeight, 0.0f, 0.0f))); joint->setChildPose(new PxTransform(new PxVec3(-boxSize, 0.0f, 0.0f))); } } gScene->addArticulation(ref *gArticulation); #if USE_REDUCED_COORDINATE_ARTICULATION gArticulation->setArticulationFlags(PxArticulationFlags.eFIX_BASE); #else // Attach articulation to static world { PxShape *anchorShape = gPhysics->createShape(new PxSphereGeometry(0.05f), *gMaterial, false, PxShapeFlags.eVISUALIZATION | PxShapeFlags.eSCENE_QUERY_SHAPE | PxShapeFlags.eSIMULATION_SHAPE); //BIOQUIRK: Missing defaults PxRigidStatic *anchor = PxCreateStatic(ref *gPhysics, new PxTransform(initPos), ref *anchorShape); gScene->addActor(ref *anchor); PxSphericalJoint *j = PxSphericalJointCreate(ref *gPhysics, anchor, new PxTransform(new PxVec3(0.0f)), firstLink, new PxTransform(new PxVec3(0.0f))); } #endif // Create obstacle { PxShape *boxShape = gPhysics->createShape(new PxBoxGeometry(1.0f, 0.1f, 2.0f), *gMaterial, false, PxShapeFlags.eVISUALIZATION | PxShapeFlags.eSCENE_QUERY_SHAPE | PxShapeFlags.eSIMULATION_SHAPE); //BIOQUIRK: Missing defaults PxRigidStatic *obstacle = PxCreateStatic(ref *gPhysics, new PxTransform(initPos.operator_Plus(new PxVec3(10.0f, -3.0f, 0.0f))), ref *boxShape); //BIOQUIRK: Overloaded operator gScene->addActor(ref *obstacle); } }