Bullet Physics: lanzar un rayo directamente desde un cuerpo rígido (cámara en primera persona)

10

Implementé una cámara en primera persona usando Bullet: es un cuerpo rígido con forma de cápsula. Solo he estado usando Bullet durante unos días y los motores de física son nuevos para mí. Solía btRigidBody::setLinearVelocity()moverlo y choca perfectamente con el mundo. El único problema es que el valor Y se mueve libremente, lo que resolví temporalmente estableciendo el valor Y del vector de traducción a cero antes de mover el cuerpo. Esto funciona para todos los casos excepto cuando cae desde una altura.

Cuando el cuerpo deja caer un objeto alto, aún puede deslizarse ya que el valor Y del vector de traslación se establece en cero, hasta que deja de moverse y cae al suelo (la velocidad solo se establece cuando se mueve). Entonces, para resolver esto, me gustaría intentar lanzar un rayo desde el cuerpo para determinar el valor Y del mundo y verificar la diferencia entre ese valor y el valor Y del cuerpo de la cámara, y desactivar o ralentizar el movimiento si La diferencia es lo suficientemente grande.

Estoy un poco atascado en simplemente lanzar un rayo y determinar el valor Y del mundo donde golpeó. He implementado esta devolución de llamada:

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;
    }
};

Y en la función de movimiento, tengo este código:

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);

Así que tengo el callback.m_hitPointWorldvector, que parece mostrar la posición de la cámara en cada fotograma. He buscado en Google ejemplos de rayos de lanzamiento, así como la documentación de Bullet, y ha sido difícil encontrar un ejemplo. Un ejemplo es realmente todo lo que necesito.

¿O tal vez hay algún método en Bullet para mantener el cuerpo rígido en el suelo?

Estoy usando Ogre3D como motor de renderizado, y lanzar un rayo hacia abajo es bastante sencillo con eso, sin embargo, quiero mantener todo el lanzamiento de rayos dentro de Bullet por simplicidad.

¿Alguien podría señalarme en la dirección correcta? Gracias.

Mago azul
fuente

Respuestas:

10

Resulta que la solución es mucho más simple que el intento original, y no hay necesidad de subclasificar si solo está probando una colisión de un rayo.

Esto es todo lo que debe hacer para recuperar el vector de colisión:

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());
}
Mago azul
fuente