Como implemento um Bullet Physics CollisionObject que representa meu cubo como terreno?

8

Integrei com sucesso a biblioteca Bullet Physics no meu sistema de entidade / componente. As entidades podem colidir umas com as outras. Agora eu preciso permitir que eles colidam com o terreno, que é finito e parecido com um cubo (pense no InfiniMiner ou no clone Minecraft ). Eu só comecei a usar a biblioteca Bullet Physics ontem, então talvez esteja perdendo algo óbvio.

Até agora, estendi a RigidBodyclasse para substituir a checkCollisionWith(CollisionObject co)função. No momento, é apenas uma simples verificação da origem, sem usar a outra forma. Vou repetir isso mais tarde. Por enquanto, fica assim:

@Override
public boolean checkCollideWith(CollisionObject co) {
    Transform t = new Transform();
    co.getWorldTransform(t);
    if(COLONY.SolidAtPoint(t.origin.x, t.origin.y,t.origin.z)){
        return true;
    }
    return false;
}

Isso funciona muito bem, tanto quanto detecta quando ocorrem colisões. No entanto, isso não lida com a resposta de colisão. Parece que a resposta de colisão padrão é mover os objetos em colisão para fora das formas uns dos outros, possivelmente seus AABBs.

No momento, a forma do terreno é apenas uma caixa do tamanho do mundo. Isso significa que as entidades que colidem com o terreno simplesmente disparam para fora dessa caixa de tamanho mundial. Portanto, fica claro que eu preciso modificar a resposta à colisão ou criar uma forma que se adapte diretamente à forma do terreno. Então, qual é a melhor opção e como implementá-la? Talvez haja uma opção em que não estou pensando?

Note-se que o terreno é dinâmico e frequentemente modificado pelo jogador.

MichaelHouse
fonte

Respostas:

8

Embora eu aprecie a resposta de Kevin Reid, ela foi em um nível superior ao que minha pergunta estava fazendo. Compreensivelmente, sem o conhecimento de Bullet Physics, seria difícil responder a essa pergunta. Eu consegui isso e tenho uma resposta que é específica para Bullet Physics.

Além de estender a RigidBodyaula como mencionei na minha pergunta. Eu também precisava estender a CollisionAlgorithmaula. Isso é principalmente para substituir a processCollision()função. Dentro da processCollision()função (que leva os dois corpos em colisão como argumentos), eu pude criar uma forma de cubo e apropriada Transformpara o cubo com o qual minha entidade estava colidindo no momento. Em seguida, deixe a colisão padrão acontecer com base na entidade e nos cubos / cubos específicos com os quais está colidindo. Para usar o recém-estendido CollisionAlgorithm, eu precisava registrar o algoritmo para lidar com as formas que eu quero que ele manipule. Nesse caso, é praticamente o tipo de terreno em relação a todo o resto. Para isso eu usei registerCollisionCreateFunc()com o meuCollisionDispatcher .

Portanto, para aqueles que acompanham no futuro:

  1. Ampliar RigidBody para ter uma verificação de colisão básica com o seu terreno.
  2. Crie uma instância da sua RigidBodyclasse e adicione-a ao seu DynamicsWorldou o que PhysicsProccesorvocê estiver usando.
  3. Amplie CollisionAlgorithm, especificamente processCollision()para criar formas e transformações de Bullet Physics que correspondam ao seu local de colisão.
  4. Registre sua versão CollisionAlgorithmcom seu CollisionDispatcherusoregisterCollisionCreateFunc() . (Esse registro é feito várias vezes, uma vez para cada par de formas que você deseja colidir.)

EDITAR

Aqui está um vídeo dele em ação, se alguém estiver interessado.

Detectando a colisão inicial

Nas minhas verificações iniciais de colisão, meu estendido rigidBodysubstitui ocheckCollideWith função descrita na minha pergunta. Eu tenho uma função para o meu terreno que pode verificar se o mundo é sólido em um ponto específico. Basicamente, testo meu terreno contra o objeto que está sendo passado pelocheckCollideWith função, verificando se meu terreno é sólido em qualquer lugar dentro dos limites desse objeto.

Agora, há também o próximo passo no Bullet, localizando os pontos de contato. Isso ocorre na processCollision()função que mencionei acima. Aqui, eu fiz uma boxShape o tamanho de um cubo de terreno, quando detecto uma colisão nocheckCollideWith função, no local da colisão e deixo o Bullet usar todos os seus algoritmos padrão para detectar os pontos de colisão .

Então, basicamente, se os limites de um objeto físico tocam em material sólido. Vou colocar meu corpo físico temporário nesse local e dizer a Bullet para verificar colisões contra esse cubo temporário, como se estivesse sempre lá. É como uma super otimização, colocando um boxShape para cada cubo no meu terreno. Em vez de milhões de boxShapes, só preciso ter um que se teletransporte quando for detectada uma colisão.

MichaelHouse
fonte
Você pode expandir mais sobre como detectou colisões em primeiro lugar?
timoxley
11
Atualizei a resposta um pouco.
Michaelhouse
Como você lidaria com um único item que colide em vários pontos, por exemplo, uma escada apoiada no chão e na parede?
timoxley
O cubo temporário é movido para todos os locais onde um objeto entra em contato com o terreno. É usado apenas para a detecção fina para obter pontos de contato e responder adequadamente.
MichaelHouse
3

Eu estava tendo alguns problemas com a estratégia implementada em minha outra resposta. Às vezes, os pontos de contato permaneciam, era meio hacky fazer outras formas que não cubos e, às vezes, permitir que objetos deslizassem pelo terreno.

Portanto, em vez de modificar ou substituir qualquer uma das classes Bullet, existe uma opção alternativa de usar um objeto de colisão incorporado no Bullet que representará o terreno. O BvhTriangleMeshShape( doc ) é uma forma construída que é representada por uma malha triangular.

Essa malha pode ser gerada ao mesmo tempo que a malha para visualizar o mundo. Isso significa que o objeto de física pode corresponder exatamente ao objeto renderizado.

Eu crio um RigidBodypara cada pedaço do meu terreno. Esse corpo tem sua forma definida como a BvhTriangleMeshShape. Quando o terreno é modificado, ao mesmo tempo em que estou reconstruindo a representação visual do pedaço, também estou reconstruindo a forma física. Então, quando chega a hora de amortecer a forma visual, também troco as formas físicas da seguinte forma:

dynamicsWorld.removeRigidBody(chunk.getRigidBody());
chunk.getRigidBody().setCollisionShape(newShape);
dynamicsWorld.addRigidBody(chunk.getRigidBody());

Isso garante que o corpo seja removido adequadamente, limpando os pontos de contato. Então, sua forma é alterada e é adicionada novamente.

Para gerar BvhTriangleMeshShapecada pedaço, é necessário manter um TriangleIndexVertexArray( doc ). Este é essencialmente dois buffers de bytes. Um com as posições dos vértices do triângulo e o outro com os índices para a construção desses triângulos. Essa matriz de vértices deve ser mantida, pois BvhTriangleMeshShapenão faz uma cópia dos dados.

Usar todas as aulas de física incorporadas do Bullet é provavelmente mais rápido do que qualquer coisa que eu pudesse escrever e, de fato, é muito rápido. Não vejo lentidão depois de implementar essa nova estratégia.

insira a descrição da imagem aqui

MichaelHouse
fonte
Observarei para quem ler isso que, pelo menos nos meus testes, o JBullet é MUITO lento em cozinhar malhas (ou seja, pré-processá-las antes que possam ser usadas para a física), pelo menos em comparação com o tempo necessário para converter um pedaço em uma malha via cubos em marcha. Estamos falando de ordens de magnitude mais lentas. Então, eu vou olhar para o PhysX e ver quanto melhor eu posso conseguir. Se alguém tiver informações sobre isso, eu adoraria ouvir.
Philip Guin
2

Não estou familiarizado com Bullet Physics, mas usei ODE. Lá, após o teste de colisão sim ou não, há um teste de colisão de forma e detalhe mais detalhado que gera um conjunto de pontos de contato.

No seu caso, seu mundo é uma coleção de caixas, então você pode fazer isso:

  1. Pegue o AABB da entidade em movimento.
  2. Itere sobre os voxels do terreno no volume que o intercepta.
  3. Para cada voxel do terreno sólido, construa uma caixa correspondente e calcule (de preferência usando as rotinas fornecidas pelo mecanismo de física) a colisão dessa caixa com a entidade em movimento.
  4. Retorne a coleção de todos os pontos de contato resultantes.

Isso não está redefinindo a resposta à colisão ; esta é uma camada antes disso. A resposta à colisão é determinada totalmente pelos pontos de contato calculados a partir da colisão.

Como eu disse, não estou familiarizado com a Bullet Physics, então não sei se a arquitetura é passível disso.

Kevin Reid
fonte
Obrigado. Eu acho que a resposta à colisão também está ligada ao teste de colisão de formas. Ele deve usar essas informações para decidir qual a maneira de separá-las e qual a distância delas, certo? Eu ficaria bem com a resposta de colisão atual se estivesse respondendo à forma do meu terreno.
MichaelHouse
Sim; Eu quis dizer que você não redefine o algoritmo de resposta a colisões, mas redefine a geração de pontos de contato que são as entradas para esse algoritmo.
Kevin Reid