Bullet Physics Simplest Collision Example

Ik probeer Bullet Physics alleen voor botsingdetectie te gebruiken. Ik heb het niet nodig om objecten voor me te verplaatsen of om met callbacks te werken. Ik wil objectlocaties elk frame updaten en gebruiken om me te vertellen wanneer ik botsingen heb. Om het eenvoudigste voorbeeld te vinden, probeer ik botsingen te vinden tussen objecten met btBoxShape als hun vorm. Alles verloopt prima zonder crashes of kennelijke geheugenlekken, maar ik krijg geen botsingen, dus ik moet ergens een fout maken. Ik zal proberen dit zo kort mogelijk te houden zonder iets belangrijks achter te laten.

Dit is mijn functie voor het instellen van de wereld:

collisionConfig      = new btDefaultCollisionConfiguration();
dispatcher           = new btCollisionDispatcher(collisionConfig);
overlappingPairCache = new btDbvtBroadphase();
solver               = new btSequentialImpulseConstraintSolver;
dynamicsWorld        = new btDiscreteDynamicsWorld(dispatcher, 
overlappingPairCache, solver, collisionConfig);         

dynamicsWorld->setGravity(btVector3(0.0f, -9.8f, 0.0f));

Op dit moment heb ik speler- en vijandelijke objecten van het type btCollisionObject *. Ik stel ze zo in:

mPlayerBox = new btBoxShape(btVector3(1,3,1));
mPlayerObject = new btCollisionObject();
mPlayerObject->setCollisionShape(mPlayerBox);
btTransform playerWorld;
playerWorld.setIdentity();
//playerPos is a D3DXVECTOR3 that holds the camera position.
playerWorld.setOrigin(btVector3(playerPos.x, playerPos.y, playerPos.z));
mPlayerObject->setWorldTransform(playerWorld);
mPlayerObject->forceActivationState(DISABLE_DEACTIVATION);//maybe not needed
dynamicsWorld->addCollisionObject(mPlayerObject);

Ik doe in wezen hetzelfde met mijn vijandelijke objecten.

Vervolgens werk ik elk frame mijn objecten bij met zoiets als dit:

btTransform updatedWorld;
updatedWorld.setIdentity();
updatedWorld.setOrigin(btVector3(position.x, position.y, position.z));
mPlayerObject->setWorldTransform(updatedWorld);

//do the same for my enemies, and then...

dynamicsWorld->performDiscreteCollisionDetection();
//Also tried doing this with stepSimulation(deltaTime, 7), but nothing changed.
//stepSimulation seems to only be for letting Bullet set world Transforms?

//check collisions with player
dynamicsWorld->contactTest(mPlayerObject, resultCallback);
int numManifolds = dynamicsWorld->getDispatcher()->getNumManifolds();
if(numManifolds > 0)
{
   //there's a collision, execute blah blah blah
}

En tot slot, hier is de structuur die mijn resultaat callback definieert:

struct rCallBack : public btCollisionWorld::ContactResultCallback
{
 btScalar rCallback::addSingleResult(btManifoldPoint& cp, const btCollisionObject*
 colObj0, int partId0, int index0, const btCollisionObject* colObj1, int partId1,
 int index1)
 {
   btVector3 ptA = cp.getPositionWorldOnA();
   btVector3 ptB = cp.getPositionWorldOnB();
   return 0;
 }
}

Ik heb veel demo's bekeken, maar ze lijken de beweging grotendeels aan Bullet over te laten, en aangezien ik karakters op een bepaalde snelheid verplaats zonder enige speciale fysica wanneer ze botsen, had ik moeite om de voorbeelden in te passen mijn aanvraag. Het resultaat callback kwam eigenlijk uit deze post op de forums: http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=6816 Het gaat om het gebruik van driehoekige mazen, maar het leek het dichtst in de buurt te komen van wat ik probeerde te implementeren.

Hoe dan ook, als je zo ver leest, bedankt !! Elk advies of links die u zou kunnen missen zou zeer op prijs worden gesteld.

8
"Ik wil objectlocaties elk frame updaten en gebruiken om me te vertellen wanneer ik botsingen heb." Dat is in het algemeen haaks op hoe een fysica -systeem werkt. Je zou moeten proberen te werken met je physics engine, niet tegen het. Als je personages hebt die met een bepaalde snelheid bewegen, moet je je fysische systeem echt laten bewegen. Het kan dat prima doen.
toegevoegd de auteur Nicol Bolas, de bron
Wat is de invoer en uitvoer? Bijv .: voor elke frame Input = positie + snelheid (per stap) van alle objecten Output = welke paren objecten botsten op welk punt in de ruimte? En u werkt de posities/snelheden handmatig bij met behulp van een aangepaste methode?
toegevoegd de auteur Ciro Santilli 包子露宪 六四事件 法轮功, de bron
ja, ik wou dat ik dit als het antwoord kon taggen. Nadat ik dit had gelezen en er nog een beetje naar had gekeken, besefte ik dat ik de botsdetectie zelf kan doen met behulp van begrenzingsvolumes en wat wiskunde. Bedankt!
toegevoegd de auteur Aztal, de bron

3 antwoord

Ik schrijf een IOS-app waarbij het lichter is om elkaar op 3D-scènes te fotograferen. Ik gebruik bullet physics voor botsingsdetectie Ik heb de flighter als een kinematisch object ingesteld, mijn logica verplaatst de flighter en werkt dan de btMotionState worldTransform van het kinematische object bij. Ik krijg ook geen botsingsdetecties totdat ik de volgende twee instructies wijzig (stel de maskering en groep in op hetzelfde voor zowel speler als vijand)

dynamicsWorld->addRigidBody(mPlayerObject,1,1);
dynamicsWorld->addRigidBody(mEnemyObject,1,1);
...
dynamicsWorld->setInternalTickCallback(myTickCallback);

dan kan ik het zien

void myTickCallback(btDynamicsWorld *world, btScalar timeStep) {
    int numManifolds = world->getDispatcher()->getNumManifolds();
    printf("numManifolds = %d\n",numManifolds);
}

De waarde numManifolds wordt 1 als het object botst.

4
toegevoegd

U kunt de contactgegevens bekijken zoals beschreven hier :

Contactgegevens

De beste manier om vast te stellen of er botsingen zijn tussen bestaande   objecten in de wereld, is het herhalen van alle contactverdelers. Deze   moet worden gedaan tijdens een simulatie tik (substep) callback, omdat   contacten kunnen tijdens verschillende substappen van een worden toegevoegd en verwijderd   single stepSimulation-oproep. Een contactverdeelstuk is een cache dat   bevat alle contactpunten tussen paren botsobjecten. Een goede   manier is om alle paren objecten in het geheel te herhalen   botsing/dynamiek wereld:

//Assume world->stepSimulation or world->performDiscreteCollisionDetection has been called

    int numManifolds = world->getDispatcher()->getNumManifolds();
    for (int i=0;igetDispatcher()->getManifoldByIndexInternal(i);
        btCollisionObject* obA = static_cast(contactManifold->getBody0());
        btCollisionObject* obB = static_cast(contactManifold->getBody1());

        int numContacts = contactManifold->getNumContacts();
        for (int j=0;jgetContactPoint(j);
            if (pt.getDistance()<0.f)
            {
                const btVector3& ptA = pt.getPositionWorldOnA();
                const btVector3& ptB = pt.getPositionWorldOnB();
                const btVector3& normalOnB = pt.m_normalWorldOnB;
            }
        }
    }

Mogelijk bent u geïnteresseerd in btGhostObject dat zijn eigen overlappende paren bijhoudt.

1
toegevoegd

Minimaal uitvoerbaar voorbeeld

Een bol die valt en de grond raakt.

Botsingen worden gedetecteerd en afgedrukt op stdout.

Gnuplot-visualisatie:

De "botsing" -lijn gaat naar 1 telkens wanneer de bol de grond raakt.

En voor kleinere restitutiecoëfficiënten ( 0,5 en 0,5 ):

Hier stopt de bal volledig met springen en raakt de grond continu aan.

Code:

#include 
#include 
#include 

#include 

#define PRINTF_FLOAT "%7.3f"

constexpr float gravity = -10.0f;
constexpr float initialY = 10.0f;
constexpr float timeStep = 1.0f/60.0f;
// TODO some combinations of coefficients smaller than 1.0
// make the ball go up higher/not lose height. Why?
constexpr float groundRestitution = 0.9f;
constexpr float sphereRestitution = 0.9f;
constexpr int maxNPoints = 500;

std::vector collisions;
void myTickCallback(btDynamicsWorld *dynamicsWorld, btScalar timeStep) {
    collisions.clear();
    int numManifolds = dynamicsWorld->getDispatcher()->getNumManifolds();
    for (int i = 0; i < numManifolds; i++) {
        btPersistentManifold *contactManifold = dynamicsWorld->getDispatcher()->getManifoldByIndexInternal(i);
       //TODO those are unused. What can be done with them?
       //I think they are the same objects as those in the main loop
       //dynamicsWorld->getCollisionObjectArray() and we could compare
       //the pointers to see which object collided with which.
        {
            const btCollisionObject *objA = contactManifold->getBody0();
            const btCollisionObject *objB = contactManifold->getBody1();
        }
        int numContacts = contactManifold->getNumContacts();
        for (int j = 0; j < numContacts; j++) {
            btManifoldPoint& pt = contactManifold->getContactPoint(j);
            const btVector3& ptA = pt.getPositionWorldOnA();
            const btVector3& ptB = pt.getPositionWorldOnB();
            const btVector3& normalOnB = pt.m_normalWorldOnB;
            collisions.push_back(ptA);
            collisions.push_back(ptB);
            collisions.push_back(normalOnB);
        }
    }
}

int main() {
    int i, j;

    btDefaultCollisionConfiguration *collisionConfiguration
            = new btDefaultCollisionConfiguration();
    btCollisionDispatcher *dispatcher = new btCollisionDispatcher(collisionConfiguration);
    btBroadphaseInterface *overlappingPairCache = new btDbvtBroadphase();
    btSequentialImpulseConstraintSolver* solver = new btSequentialImpulseConstraintSolver;
    btDiscreteDynamicsWorld *dynamicsWorld = new btDiscreteDynamicsWorld(
            dispatcher, overlappingPairCache, solver, collisionConfiguration);
    dynamicsWorld->setGravity(btVector3(0, gravity, 0));
    dynamicsWorld->setInternalTickCallback(myTickCallback);
    btAlignedObjectArray collisionShapes;

   //Ground.
    {
        btTransform groundTransform;
        groundTransform.setIdentity();
        groundTransform.setOrigin(btVector3(0, 0, 0));
        btCollisionShape* groundShape;
#if 1
       //x/z plane at y = -1.
        groundShape = new btStaticPlaneShape(btVector3(0, 1, 0), -1);
#else
       //A cube of width 10 at y = -6.
       //Does not fall because we won't call:
       //colShape->calculateLocalInertia
       //TODO: remove this from this example into a collision shape example.
        groundTransform.setOrigin(btVector3(0, -6, 0));
        groundShape = new btBoxShape(
                btVector3(btScalar(5.0), btScalar(5.0), btScalar(5.0)));

#endif
        collisionShapes.push_back(groundShape);
        btDefaultMotionState* myMotionState = new btDefaultMotionState(groundTransform);
        btRigidBody::btRigidBodyConstructionInfo rbInfo(0, myMotionState, groundShape, btVector3(0, 0, 0));
        btRigidBody* body = new btRigidBody(rbInfo);
        body->setRestitution(groundRestitution);
        dynamicsWorld->addRigidBody(body);
    }

   //Sphere.
    {
        btCollisionShape* colShape = new btSphereShape(btScalar(1.0));
        collisionShapes.push_back(colShape);
        btTransform startTransform;
        startTransform.setIdentity();
        startTransform.setOrigin(btVector3(0, initialY, 0));
        btVector3 localInertia(0, 0, 0);
        btScalar mass(1.0f);
        colShape->calculateLocalInertia(mass, localInertia);
        btDefaultMotionState *myMotionState = new btDefaultMotionState(startTransform);
        btRigidBody *body = new btRigidBody(btRigidBody::btRigidBodyConstructionInfo(
                mass, myMotionState, colShape, localInertia));
        body->setRestitution(sphereRestitution);
        dynamicsWorld->addRigidBody(body);
    }

   //Main loop.
    std::printf("step body x y z collision a b normal\n");
    for (i = 0; i < maxNPoints; ++i) {
        dynamicsWorld->stepSimulation(timeStep);
        for (j = dynamicsWorld->getNumCollisionObjects() - 1; j >= 0; --j) {
            btCollisionObject *obj = dynamicsWorld->getCollisionObjectArray()[j];
            btRigidBody *body = btRigidBody::upcast(obj);
            btTransform trans;
            if (body && body->getMotionState()) {
                body->getMotionState()->getWorldTransform(trans);
            } else {
                trans = obj->getWorldTransform();
            }
            btVector3 origin = trans.getOrigin();
            std::printf("%d %d " PRINTF_FLOAT " " PRINTF_FLOAT " " PRINTF_FLOAT " ",
                    i,
                    j,
                    float(origin.getX()),
                    float(origin.getY()),
                    float(origin.getZ()));
            if (collisions.empty()) {
                std::printf("0 ");
            } else {
                std::printf("1 ");
               //Yes, this is getting reprinted for all bodies when collisions happen.
               //It's just a quick and dirty way to visualize it, should be outside
               //of this loop normally.
                for (auto& v : collisions) {
                    std::printf(
                            PRINTF_FLOAT " " PRINTF_FLOAT " " PRINTF_FLOAT " ",
                            v.getX(), v.getY(), v.getZ());
                }
            }
            puts("");
        }
    }

   //Cleanup.
    for (i = dynamicsWorld->getNumCollisionObjects() - 1; i >= 0; --i) {
        btCollisionObject* obj = dynamicsWorld->getCollisionObjectArray()[i];
        btRigidBody* body = btRigidBody::upcast(obj);
        if (body && body->getMotionState()) {
            delete body->getMotionState();
        }
        dynamicsWorld->removeCollisionObject(obj);
        delete obj;
    }
    for (i = 0; i < collisionShapes.size(); ++i) {
        delete collisionShapes[i];
    }
    delete dynamicsWorld;
    delete solver;
    delete overlappingPairCache;
    delete dispatcher;
    delete collisionConfiguration;
    collisionShapes.clear();
}

Based on: http://www.bulletphysics.org/mediawiki-1.5.8/index.php

Version of this focused on distinguishing which object touched which object: https://gamedev.stackexchange.com/a/120881/25171

GitHub upstream: https://github.com/cirosantilli/cpp-cheat/blob/503a3b6487ccb75334798839b5ed912270446d14/bullet/ground_ball.cpp

Getest op Bullet 2.83, Ubuntu 15.10.

0
toegevoegd