El estado de cada primitiva física está definido por su posición y orientación en un tiempo determinado. El motor físico es el encargado de calcular el nuevo estado de los objetos luego de cada actualización; y el motor gráfico, el de visualizar los objetos en la posición correcta. Entonces, el motor físico realiza la actualización del estado para un tiempo determinado, el motor gráfico solicita los datos asociados a cada objeto al motor físico, aplica los nuevos valores de posición y orientación y los dibuja en pantalla [26]. En la Figura 4.17 se esquematiza la interacción entre los motores.
Figura 4.17: Comunicación de datos entre un motor gráfico y un motor físico
El detalle de las transiciones se describen a continuación:
1. El motor físico calcula el próximo estado del mundo virtual.
2. El motor gráfico solicita los datos de los objetos al motor físico.
3. El motor físico devuelve los datos solicitados al motor gráfico.
4. El motor gráfico aplica los valores de posición y rotación a los objetos 3D.
CAPÍTULO 4. INSTANCIACIÓN
Integración de Ogre3D con Bullet Physics
Los objetos gráficos de Ogre3D y los cuerpos físicos de Bullet son entidades completamente distintas y separadas. Si no se implementa algún mecanismo mediante el cual Bullet actualice la posición y rotación de dicho objeto gráfico, se creará una situación de incoherencia en la cual la posición del objeto gráfico permanecerá estática, al contrario que la del cuerpo físico que se verá actualizada. Esto provocará que la simulación no se actualice, ya que Ogre es el encargado de renderizar los objetos 3D, mientras que Bullet tiene la responsabilidad de modelar un comportamiento realista sobre los cuerpos.
Para resolver el problema de la integración entre Bullet Physics y Ogre3D, los cuerpos rígidos de Bullet cuentan con un atributo llamado estado de movimientoMotionState[27]. Este
MotionStateabstrae de las operaciones de bajo nivel que se realiza cuando el cuerpo físico recibe la influencia de alguna fuerza. De esta forma, se puede trabajar directamente con los cuerpos físicos, sabiendo que las posiciones de los elementos de la escena serán adecuadamente actualizadas en función del movimiento del cuerpo físico.
En resumen, la claseMotionStatenos permite actualizar la posición de los nodos de escena de Ogre. Para ello, se requiere adoptar una implementación, dado que se trata de una clase abstracta. Dicha interfaz se encuentra implementada en la claseCustomMotionState.
1 v o i d CustomMotionState : : SetWorldTransform ( c o n s t btTransform& worldTrans ) {
2 / / A c t u a l i z o l a o r i e n t a c i o n d e l o b j e t o 3D, a p a r t i r de l a o r i e n t a c i o n d e l cuerpo f i s i c o 3 btQuaternion r o t = worldTrans . g e t R o t a t i o n ( ) ; 4 mSceneNode−>s e t O r i e n t a t i o n ( r o t .w( ) , r o t . x ( ) , r o t . y ( ) , r o t . z ( ) ) ; 5 6 / / A c t u a l i z o l a p o s i c i o n d e l o b j e t o 3D, a p a r t i r de l a p o s i c i o n d e l cuerpo f i s i c o
7 btVector3 pos = worldTrans . g e t O r i g i n ( ) ;
8 mSceneNode−> s e t P o s i t i o n ( pos . x ( ) , pos . y ( ) , pos . z ( ) ) ;
9 }
Algoritmo 4.9: CustomMotionState::SetWorldTransform
Bullet invoca de forma automática el métodoSetWorldTransformde cada uno de los cuerpos físicos que forman el mundo. El método recibe un objeto de tipo btTransform. La clase btTransform encapsula un cuaternio, que almacena la rotación del cuerpo físico; y un vector, que almacena su posición. Dado que la clase MotionState almacena un puntero a un nodo de escena de Ogre3D, en el momento en que Bullet invoque el métodoSetWorldTransform, se actualizará la rotación y posición de dicho nodo de escena; es decir, se actualizará la posición y rotación del cuerpo gráfico a partir de la nueva información recibida.
Por ejemplo, creamos la representación visual y física de la pelota de tenis:
4.3. VISUALIZADOR 3D
1 / / Creamos e l o b j e t o 3D de l a p e l o t a de t e n i s
2 Entity* e n t i t y T e n n i s B a l l = mSceneManager−>c r e a t e E n t i t y (’ EntityTennisBall ’, ’ b a l l . mesh ’) ;
3 SceneNode* sceneNodeTennisBall = mSceneManager−>createSceneNode (’ NodeTennisBall ’) ;
4 sceneNodeTennisBall−>a t t a c h O b j e c t ( e n t i t y T e n n i s B a l l ) ;
5
6 / / Creamos e l cuerpo f i s i c o de l a p e l o t a de t e n i s , e l c u a l sera una e s f e r a .
7 / / Por parametro , se i n d i c a e l r a d i o de l a e s f e r a , en metros .
8 btSphereShape* sphere = new btSphereShape ( 0 . 0 4 )
9 10 / / btTransform c o n t i e n e l a transformacion i n i c i a l d e l o b j e t o f i s i c o 11 btTransform transform ; 12 transform . s e t I d e n t i t y ( ) ; 13 14 f l o a t mass = 0 . 1 7 ; 15 btVector3 i n e r t i a ( 0 , 0 , 0 ) ; 16 sphere−>C a l c u l a t e L o c a l I n e r t i a ( mass , i n e r t i a ) ; 17
18 / / Unimos e l o b j e t o 3D con su cuerpo f i s i c o para s i n c r o n i z a r sus p o s i c i o n e s y o r i e n t a c i o n e s
19 MotionState* motionState = new CustomMotionState ( transform , sceneNodeTennisBall ) ;
20
21 btR igidBo dyCon struct ionIn fo r b I n f o ( mass , motionState , sphere, i n e r t i a ) ;
22 r b I n f o . m _ f r i c t i o n = 0 . 5 ;
23 r b I n f o . m _ r o l l i n g F r i c t i o n = 0 . 6 ;
24 r b I n f o . m _ r e s t i t u t i o n = 0 . 7 ;
25
26 / / Creamos e l cuerpo r i g i d o y l o a\~nadimos a l mundo f i s i c o
27 btRigidBody* r i g i d b o d y = new btRigidBody ( r b I n f o ) ;
28 mDynamicsWorld−>addRigidBody ( rigidBody ) ;
Algoritmo 4.10: Algoritmo para crear representación visual y física de la pelota
El constructor de la clasebtRigidBodyrecibe un objetobtRigidBodyConstructionInfo. Este objeto sirve para inyectar al constructor de la clase información relativa al cuerpo rígido que se va a crear. Los argumentos que recibe son la masa del objeto, el estado del cuerpo (CustomMotionS- tate), la forma física del cuerpo (btSphereShape) y el vector de inercia. A partir de la forma del cuerpo y de la masa de éste, Bullet calcula la inercia del cuerpo físico que estamos construyendo a través del métodobtCollisionShape::CalculateLocalInertia. Una vez creado el cuerpo rígido, hay que añadirlo al mundo físico a través del método btDiscreteDynamicsWorld::addRigidBody.
En la Figura 4.18 se pueden apreciar los distintos objetos que están situados en el escenario, los cuales contienen unas líneas verdes en sus bordes. Estas líneas representan los objetos físicos dentro del escenario, que como se observa se corresponden con la visual. Por ejemplo, un ladrillo
CAPÍTULO 4. INSTANCIACIÓN
Figura 4.18: Visualización física de los objetos
coordenadas para objetos dinámicos, que significa que tienen un comportamiento físico que al aplicarle una fuerza cobran movimiento.
Otra observación que se puede hacer con los objetos es comparar una silla con un ladrillo. Como dijimos anteriormente, el ladrillo es una caja, una primitiva simple del motor físico; en cambio, la silla está creada a partir de una primitiva compleja llamadabtBvhTriangleMeshShape, donde el cuerpo físico comparte la forma geométrica del objeto 3D.