Bullet Physics - Rzucanie promienia prosto ze sztywnego ciała (kamera FPS)

10

Zaimplementowałem aparat FPS za pomocą Bulleta - jest to sztywny korpus o kształcie kapsułki. Używam Bulleta od kilku dni, a silniki fizyki są dla mnie nowe. Używam go, btRigidBody::setLinearVelocity()aby go przenieść i idealnie zderza się ze światem. Jedyny problem polega na tym, że wartość Y porusza się swobodnie, co chwilowo rozwiązałem, ustawiając wartość Y wektora translacji na zero przed przeniesieniem ciała. Działa to we wszystkich przypadkach, z wyjątkiem upadku z wysokości.

Kiedy ciało spadnie z wysokiego obiektu, nadal możesz się szybować, ponieważ wartość Y wektora translacji jest ustawiana na zero, dopóki nie przestaniesz się poruszać i spadniesz na ziemię (prędkość jest ustawiana tylko podczas ruchu). Aby rozwiązać ten problem, chciałbym spróbować rzucić promień w dół z ciała, aby określić wartość Y świata i sprawdzić różnicę między tą wartością a wartością Y korpusu kamery oraz wyłączyć lub spowolnić ruch, jeśli różnica jest wystarczająco duża.

Trochę utknąłem po prostu rzucając promień i określając wartość Y świata, w którym uderzył. Zaimplementowałem to wywołanie zwrotne:

struct AllRayResultCallback : public btCollisionWorld::RayResultCallback{
    AllRayResultCallback(const btVector3& rayFromWorld, const btVector3& rayToWorld)
        : m_rayFromWorld(rayFromWorld), m_rayToWorld(rayToWorld), m_closestHitFraction(1.0){}

    btVector3   m_rayFromWorld;
    btVector3   m_rayToWorld;
    btVector3   m_hitNormalWorld;
    btVector3   m_hitPointWorld;
    float       m_closestHitFraction;

    virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace)
    {
        if(rayResult.m_hitFraction < m_closestHitFraction)
            m_closestHitFraction = rayResult.m_hitFraction;

        m_collisionObject = rayResult.m_collisionObject;
        if(normalInWorldSpace){
            m_hitNormalWorld = rayResult.m_hitNormalLocal;
        }
        else{
            m_hitNormalWorld = m_collisionObject->getWorldTransform().getBasis() * rayResult.m_hitNormalLocal;
        }

        m_hitPointWorld.setInterpolate3(m_rayFromWorld, m_rayToWorld, m_closestHitFraction);
        return 1.0f;
    }
};

A w funkcji ruchu mam ten kod:

btVector3 from(pos.x, pos.y + 1000, pos.z); // pos is the camera's rigid body position
btVector3 to(pos.x, 0, pos.z); // not sure if 0 is correct for Y

AllRayResultCallback callback(from, to);
Base::getSingletonPtr()->m_btWorld->rayTest(from, to, callback);

Mam więc callback.m_hitPointWorldwektor, który wydaje się po prostu pokazywać pozycję kamery w każdej klatce. Szukałem w Google przykładów promieni rzucania, a także dokumentacji Bullet, i ciężko było znaleźć przykład. Przykład jest naprawdę wszystkim, czego potrzebuję.

A może jest jakaś metoda Bulleta, aby utrzymać sztywne ciało na ziemi?

Używam Ogre3D jako silnika renderującego i rzucanie promienia w dół jest dość proste, jednak chcę zachować cały rzut w Bullet dla uproszczenia.

Czy ktoś mógłby skierować mnie we właściwym kierunku? Dzięki.

Blue Wizard
źródło

Odpowiedzi:

10

Okazuje się, że rozwiązanie jest znacznie prostsze niż oryginalna próba i nie ma potrzeby podklasowania, jeśli testujesz tylko jedno zderzenie promienia.

Oto wszystko, co musisz zrobić, aby pobrać wektor kolizji:

btVector3 btFrom(camPos.x, camPos.y, camPos.z);
btVector3 btTo(camPos.x, -5000.0f, camPos.z);
btCollisionWorld::ClosestRayResultCallback res(btFrom, btTo);

Base::getSingletonPtr()->m_btWorld->rayTest(btFrom, btTo, res); // m_btWorld is btDiscreteDynamicsWorld

if(res.hasHit()){
    printf("Collision at: <%.2f, %.2f, %.2f>\n", res.m_hitPointWorld.getX(), res.m_hitPointWorld.getY(), res.m_hitPointWorld.getZ());
}
Blue Wizard
źródło