Bullet Physics - Lançando um raio diretamente de um corpo rígido (câmera em primeira pessoa)

10

Eu implementei uma câmera em primeira pessoa usando o Bullet - é um corpo rígido com formato de cápsula. Só uso o Bullet há alguns dias e os mecanismos de física são novos para mim. Eu uso btRigidBody::setLinearVelocity()para movê-lo e ele colide perfeitamente com o mundo. O único problema é que o valor Y se move livremente, o que resolvi temporariamente definindo o valor Y do vetor de conversão como zero antes de o corpo ser movido. Isso funciona para todos os casos, exceto quando cai de uma altura.

Quando o corpo cai de um objeto alto, você ainda pode deslizar ao redor, já que o valor Y do vetor de conversão está sendo definido como zero, até você parar de se mover e cair no chão (a velocidade é definida apenas ao se mover). Então, para resolver isso, eu gostaria de tentar lançar um raio do corpo para determinar o valor Y do mundo e verificar a diferença entre esse valor e o valor Y do corpo da câmera e desativar ou desacelerar o movimento se a diferença é grande o suficiente.

Estou um pouco preso em simplesmente lançar um raio e determinar o valor Y do mundo onde ele atingiu. Eu implementei esse retorno de chamada:

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

E na função de movimento, eu tenho 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);

Então, eu tenho o callback.m_hitPointWorldvetor, que parece apenas mostrar a posição da câmera em cada quadro. Pesquisei no Google por exemplos de raios de projeção, bem como a documentação do Bullet, e tem sido difícil encontrar apenas um exemplo. Um exemplo é realmente tudo o que preciso.

Ou talvez exista algum método no Bullet para manter o corpo rígido no chão?

Estou usando o Ogre3D como um mecanismo de renderização, e lançar um raio é bastante direto com isso, no entanto, quero manter todo o raio dentro do Bullet por simplicidade.

Alguém poderá me indicar a direção correta? Obrigado.

Assistente azul
fonte

Respostas:

10

Acontece que a solução é muito mais simples que a tentativa original e não há necessidade de subclassificar se você estiver apenas testando uma colisão de raios.

Aqui está tudo o que você deve fazer para recuperar o vetor de colisão:

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());
}
Assistente azul
fonte