¿Cómo implemento un objeto de colisión Bullet Physics que representa mi cubo como terreno?

8

He integrado con éxito la biblioteca Bullet Physics en mi sistema de entidad / componente. Las entidades pueden chocar entre sí. Ahora necesito permitirles colisionar con el terreno, que es finito y en forma de cubo (piense en InfiniMiner o en el clon Minecraft ). Apenas comencé a usar la biblioteca Bullet Physics ayer, así que quizás me estoy perdiendo algo obvio.

Hasta ahora he extendido la RigidBodyclase para anular la checkCollisionWith(CollisionObject co)función. Por el momento, es solo una simple verificación del origen, sin usar la otra forma. Voy a repetir eso más tarde. Por ahora se ve así:

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

Esto funciona muy bien, en cuanto a detectar cuándo ocurren las colisiones. Sin embargo, esto no maneja la respuesta de colisión. Parece que la respuesta de colisión predeterminada es mover los objetos en colisión fuera de las otras formas, posiblemente sus AABB.

Por el momento, la forma del terreno es solo una caja del tamaño del mundo. Esto significa que las entidades que colisionan con el terreno simplemente se disparan fuera de esa caja de tamaño mundial. Entonces, está claro que necesito modificar la respuesta de colisión o necesito crear una forma que se adapte directamente a la forma del terreno. Entonces, ¿qué opción es mejor y cómo hago para implementarla? ¿Quizás hay una opción en la que no estoy pensando?

Cabe señalar que el terreno es dinámico y frecuentemente modificado por el jugador.

MichaelHouse
fuente

Respuestas:

8

Si bien aprecio la respuesta de Kevin Reid, fue a un nivel superior al que estaba formulando mi pregunta. Es comprensible que sin tener conocimiento de Bullet Physics, sería difícil responder a esta pregunta. Lo hice funcionar y tengo una respuesta que es específica de Bullet Physics.

Junto con extender la RigidBodyclase como mencioné en mi pregunta. También necesitaba extender la CollisionAlgorithmclase. Esto es principalmente para anular la processCollision()función. Dentro de la processCollision()función (que toma los dos cuerpos en colisión como argumentos), pude crear una forma de cubo y apropiada Transformpara el cubo con el que mi entidad estaba colisionando actualmente. Luego, deje que ocurra la colisión predeterminada en función de la entidad y el cubo / cubos específicos con los que está colisionando. Para usar el recién extendido CollisionAlgorithm, necesitaba registrar el algoritmo para manejar las formas que quiero que maneje. En este caso, ese es el tipo de terreno en comparación con todo lo demás. Para eso lo usé registerCollisionCreateFunc()con mi CollisionDispatcher.

Entonces, para aquellos que sigan en el futuro:

  1. Extienda RigidBodypara tener un control básico de colisión con su terreno.
  2. Crea una instancia de tu RigidBodyclase y agrégala a tu DynamicsWorldo lo PhysicsProccesorque sea que estés usando.
  3. Amplíe CollisionAlgorithm, específicamente processCollision()para crear formas y transformaciones de Bullet Physics que coincidan con su ubicación de colisión.
  4. Registre su versión de CollisionAlgorithmcon su CollisionDispatcheruso registerCollisionCreateFunc(). (Este registro se realiza varias veces, una vez por cada par de formas que desea colisionar).

EDITAR

Aquí hay un video de él en acción si alguien está interesado.

Detectando la colisión inicial

Para mis comprobaciones de colisión iniciales, mi extendido rigidBodyanula la checkCollideWithfunción descrita en mi pregunta. Tengo una función para mi terreno que puede verificar si el mundo es sólido en un punto específico. Básicamente, pruebo mi terreno contra el objeto que pasa la checkCollideWithfunción, para ver si mi terreno es sólido en algún lugar dentro de los límites de ese objeto.

Ahora, también está el siguiente paso en Bullet, encontrar los puntos de contacto. Esto tiene lugar en la processCollision()función que mencioné anteriormente. Aquí, hice una caja de forma del tamaño de un cubo de terreno, luego, cuando detecto una colisión en la checkCollideWithfunción, coloco esa caja de tamaño de cubo de terreno en la ubicación de colisión y dejo que Bullet use todos sus algoritmos predeterminados para detectar puntos de colisión allí .

Básicamente, si los límites de un objeto físico tocan material sólido. Colocaré mi cuerpo de física temporal en ese lugar y le diré a Bullet que verifique las colisiones contra ese cubo temporal, como si siempre estuviera allí. Esto es como una súper optimización colocando un boxShape para cada cubo en mi terreno. En lugar de millones de boxShapes, solo necesito tener uno que se teletransporte cuando se detecte una colisión.

MichaelHouse
fuente
¿Puede ampliar más sobre cómo detectó colisiones en primer lugar?
timoxley
1
@timoxley He actualizado un poco la respuesta.
MichaelHouse
¿Cómo manejaría un solo elemento que colisiona en múltiples puntos, por ejemplo, una escalera apoyada en el suelo y en una pared?
timoxley
El cubo temporal se mueve para todos los lugares donde un objeto contacta con el terreno. Solo se usa para la detección fina para obtener puntos de contacto y responder adecuadamente.
MichaelHouse
3

Estaba teniendo algunos problemas con la estrategia implementada en mi otra respuesta. Los puntos de contacto a veces se quedaban, era un poco raro hacer formas que no fueran cubos y a veces permitía que los objetos se deslizaran por el terreno.

Entonces, en lugar de modificar o anular cualquiera de las clases Bullet, existe una opción alternativa de usar un objeto de colisión Bullet incorporado que representará el terreno. El BvhTriangleMeshShape( doc ) es una forma integrada que está representada por una malla triangular.

Esta malla se puede generar al mismo tiempo que la malla para visualizar el mundo. Esto significa que el objeto físico puede coincidir exactamente con el objeto renderizado.

Creo un RigidBodypara cada trozo en mi terreno. Ese cuerpo tiene su forma establecida en a BvhTriangleMeshShape. Cuando se modifica el terreno, al mismo tiempo que estoy reconstruyendo la representación visual del fragmento, también estoy reconstruyendo la forma física. Luego, cuando llega el momento de amortiguar la forma visual, también cambio las formas físicas de la siguiente manera:

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

Esto asegura que el cuerpo se retire correctamente, limpiando los puntos de contacto. Luego se cambia su forma y se vuelve a agregar.

Para generar BvhTriangleMeshShapecada fragmento debe mantener un TriangleIndexVertexArray( doc ). Esto es esencialmente dos buffers de byte. Uno con las posiciones de los vértices del triángulo y el otro con los índices para construir esos triángulos. Esta matriz de vértices debe mantenerse ya BvhTriangleMeshShapeque no hace una copia de los datos.

El uso de todas las clases de física de Bullet integradas es probablemente más rápido que cualquier cosa que pueda escribir, y de hecho funciona muy rápido. No he visto ninguna desaceleración después de implementar esta nueva estrategia.

ingrese la descripción de la imagen aquí

MichaelHouse
fuente
Notaré a cualquiera que lea esto que, al menos en mis pruebas, JBullet es MUY lento para cocinar mallas (es decir, preprocesarlas antes de que puedan usarse para la física), al menos en comparación con el tiempo que me toma convertir un trozo en una malla a través de cubos de marcha. Estamos hablando de órdenes de magnitud más lento. Por lo tanto, voy a analizar PhysX y ver cuánto mejor puedo lograr que funcione. Si alguien tiene información sobre esto, me encantaría escucharlo.
Philip Guin
2

No estoy familiarizado con Bullet Physics, pero he usado ODE. Allí, después de la prueba de colisión sí o no, hay una prueba de colisión de forma-forma más detallada que genera un conjunto de puntos de contacto.

En su caso, su mundo es una colección de cajas, por lo que podría hacer esto:

  1. Tome el AABB de la entidad en movimiento.
  2. Iterar sobre los vóxeles del terreno en el volumen que lo cruza.
  3. Para cada vóxel del terreno que es sólido, construya una caja coincidente y calcule (preferiblemente usando las rutinas proporcionadas por el motor de física) la colisión de esa caja con la entidad en movimiento.
  4. Devuelva la colección de todos los puntos de contacto resultantes.

Esto no está redefiniendo la respuesta de colisión ; Esta es una capa antes de eso. La respuesta de colisión se determina completamente por los puntos de contacto calculados a partir de la colisión.

Como dije, no estoy familiarizado con Bullet Physics, por lo que no sé si su arquitectura es adecuada para esto.

Kevin Reid
fuente
Gracias. Creo que la respuesta de colisión también está vinculada a la prueba de colisión de forma. Debe usar esa información para decidir de qué manera separarlos y hasta qué punto separarlos, ¿verdad? Estaría bien con la respuesta de colisión actual si respondiera a la forma de mi terreno.
MichaelHouse
Si; Quise decir que no redefine el algoritmo de respuesta de colisión, sino que redefine la generación de puntos de contacto que son las entradas a ese algoritmo.
Kevin Reid