Tarea 7. Objetos Deformables. (Tetraedro).

Oliver Fernando Cuate González

ocuate@computacion.cs.cinvestav.mx

19 de Diciembre de 2013


Problema


Marco Teórico

Sistemas Masa Resorte:

Este modelo deformable consiste en la coonexión de vértices con una determinada masa por medio de resortes sin masa. Al aplicar una fuerza sobre un vértice, el sistema se actualiza según las propiedades de los resortes definidos.

El modelo masa resorte oscilaría por siempre al no considerarse fuerzas como la fricción en su repesentación, es por eso que el modelo que busca resolverse es uno Masa-Resorte con Amortiguador.

Aquí m es la masa de cada vértice, b es el amortiguador y k es la constante del resorte. Se tienen también las variables x que denota el desplazamiento y Fext es la fuerza externa que produce este desplazamiento.

La ecuación de este modelo está dada por la siguiente ecuación:

Una alternativa de resolver esta ecuación de forma discreta es utilizando el método de diferencias finitas. Este método utiliza la definición de derivada como un límite, pero en lugar de considerar incrementos de la función infinitamente pequeños, se toma un incremento que sea lo suficientemente pequeño para los fines prácticos que se buscan. Para este modelo se tiene:

Lo que se desea es conocer la posición de cada vértice con masa en un determinado instante de tiempo. Luego de realizar las sustituciones necesarias y de un poco algébra se llega a que una solución para este modelo en función de las posiciones anteriores en dos tiempos consecutivos anteriores al deseado con la siguiente expresión:

Con esta expresión y una definción de una malla de resortes es posible resolver el problemma de simular un objeto deforable.

Análisis

Detección de Colisiones

Para resolver el problema planteado se necesita hacer algunas modificaciones a la Tarea 6. La escena cambia con la incorporación del objeto deformable. El puntero no debe atravesar el objeto, por lo que la detección de colisiones cambiará.

El objeto deformable para el caso de este trabajo es un Tetraedro, este es un sólido con cuatro caras triangulares. El puntero, aunque se trata de un cono, tendrá el comportamiento de un rayo al momento de detectar la colisión con alguno de los vértices del Tetraedro, pero seguirá contando con un sólido envolvente (una esfera) para el resto de las colisiones.

Cada vértice del Tetraedro contará con una pequeña esfera que permitirá ocupar la colisión Esfera-Rayo para detectar si un vértice está siendo apuntado por el puntero. Sin embargo, esta prueba no detecta que tan cerca se encuentra el puntero de algún vértice. Para resolver este problemma se se emplea una colisión Esfera-Esfera entre la esfera que envuelve el cono del puntero y cada una de las esferas correspondientes a cada vértice del Tetraedro. Primero se verifica esta colisión por ser más barata, hasta que la esfera del puntero colisiona con alguna esfera se verifica si el puntero apunta al vértice. Si el puntero choca con un vértice se detiene el movimiento del mismo y no podrá avanzar más.

Para que el puntero no atraviese el objeto se realiza primero una detección Esfera-Esfera entre la esfera que envuelve el puntero y una esfera inscrita en el Tetraedro, si esta colisión se da, se verifica si hay una colisión del tipo Segmento de Recta-Triángulo entre el puntero y alguna de las caras del Tetraedro. El movimiento del puntero también se detendrá si esto ocurre.

Iluminación

A direrencia de otras tareas, las normales de las caras de los objetos deben calcularse cada que se actualiza la pantalla para obtener un efecto de iluminación adecuado. Anteriormente se realizaban movimientos a los objetos con rotaciones y traslaciones que afectaban también a las normales, pero en este caso, el efecto producido por la deformación no afecta de forma directa a las normales los objetos. Por esta razón deben calcularse cada vez que los vértices cambién su posición.

Deformación

Para realizar la deformación es necesario implementar un sistema de Masa-Resorte con Amortiguador MRA. Una alterntiva para realizar lo anterior se proporciona en la siguiente estrutura, esta es un modificación a la estructura encontrada en Mecate 2008 [1] y a un ejemplo visto en clase:
	
typedef struct mra {
    double fx, fy, fz;
    double x_1, x, x1;
    double y_1, y, y1;
    double z_1, z, z1;
    double px, py, pz;
} MRA;

typedef struct mra3d {
    double k, m, b;
    double dt;
    double k1, k2, k3, k4;
    double f, theta;

    MRA rs[4];
} MRA3D;

Se asociará cada vértice del Tetraedro una estructura de este tipo con masa m, constante del resorte k y amortiguador b iguales para cada vértice. El cambio principal entre estos elementos es la posición de cada uno de ellos.

Si se aplica una fuerza, el sistema se comportará como un sólido deformable. Para lograrlo se resuelve la ecuación diferencial asociada al sistema MRA por el método de diferencias finitas y se actualizan las variables que corresponden a la posición en el tiempo t+1, t y t-1.

Si no se aplica una fuerza y la posición actual de los vértices no coincide con la posición inicial del sistema en estado de reposo, con el paso del tiempo el sistema volverá a dicha posición. La posición en el tiempo t es la que se visualiza en la escena.

Para que el objeto interactúe con el dispositivo Patriot una vez que se detecta la colisión con un vértice, se pasa el control del dispositivo del puntero al vértice tocado cuando se da click en el botón del dispositivo. El puntero se oculta para ser repintado en su posición inicial luego de soltar el vértice, de esta forma las colisiones no limitarán el movimiento del vértice seleccionado.

Para controlar la fuerza aplicada es necesario controlar el desplazamiento del véctice seleccionado. En un resorte la fuerza está dada por: F = kx . Al momento de tomar el vértice por primera vez, se fija la posición en que el dispositivo Patriot se encuentra, este es un punto de referencia; cada que se actualiza la posición del Patriot se realiza una resta de la posición actual y la posiión de referencia, sa calcula la norma de este vector y si es menor a cierto valor el vértice continuará moviéndose. Se suma este vector desplazamiento a la posición original del vértice y se dibuja en la pantalla. Esta solución presenta el único inconveniente de que se puede producir un salto bajo movimientos muy bruscos del dispositivo Patriot.

Cuando el botón del Patriot se suelta, la ecuación diferencial continua resolviéndose y el objeto deformable volverá a su posición original, no importa si fue "jalado" o "comprimido".

Dispositivo Patriot

El manejo del dispositvo Patriot se especifica en las tareas anteriores Tarea 5 y Tarea 6 . Se explicará a continuación el sistema de referencia utilizado.

La caja del dispositivo se debe colocar de tal forma que el eje y positivo apunte hacia uno mismo y el eje x poitivo hacia la derecha. Esto por la forma en que los ejes son puestos en la caja. De esta forma el producto cruz entre estos dos vectores hace que el eje z positivo apunte hacia abajo.

La forma de relacionar estas coordenadas con el mundo virtual es asignando las coordenadas x del dispositivo a las x del mundo virtual, las coordenadas en y del dispositivo a las z del mundo virtual y cambiando el signo de la coordenada z del dispositivo y asignándola a la coordenada y del mundo virtual.

El origen de este sistema se fija al momento de dar click al botón siempre y cuando no se detecte una colisón. Todos los movimientos que se realicen a partir de este momento serán en relación a este punto definido previamente respetando la convención antes mencionada.


Alternativa de Solución en Qt y OpenGL

Visualización e Iluminación

El siguiente fragmento de código es la forma de calcular las normales cada que se actualiza la posición de los vértices, así como de dibujar el Tetraedro utilizando la iluminación.

    XYZ u,v,nor;
    int i,j;
    int cara[4][3] = {{3,0,1}, {3,1,2},{3,0,2},{0,1,2}};

    double px, py, pz;

    GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat mat_shininess[] = { 100.0 };
    GLfloat mat_ambient[] = { 0.0, 0.54510, 0.0, 1.0 };

    glMaterialfv( GL_FRONT, GL_AMBIENT, mat_ambient);
    glMaterialfv( GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv( GL_FRONT, GL_SHININESS, mat_shininess);

    //Actualizar los vértices que se dibujarán
    glPointSize( 3.0 );
    glBegin( GL_POINTS );
    for ( i=0; i<4; i++ ){
        px = spr.rs[i].px + spr.rs[i].x1;
        py = spr.rs[i].py + spr.rs[i].y1;
        pz = spr.rs[i].pz + spr.rs[i].z1;
        tetra[i][0] = px;
        tetra[i][1] = py;
        tetra[i][2] = pz;
        glVertex3d( px, py, pz );
    }
    glEnd();

    //Cálculo de normales
    for ( i=0; i<4; i++){
        u.x = tetra[cara[i][0]][0] - tetra[cara[i][1]][0];
        v.x = tetra[cara[i][2]][0] - tetra[cara[i][1]][0];

        u.y = tetra[cara[i][0]][1] - tetra[cara[i][1]][1];
        v.y = tetra[cara[i][2]][1] - tetra[cara[i][1]][1];

        u.z = tetra[cara[i][0]][2] - tetra[cara[i][1]][2];
        v.z = tetra[cara[i][2]][2] - tetra[cara[i][1]][2];

        CROSSPROD(u,v,&nor);
        Normalize(&nor);

        nTetra[i][0] = nor.x;
        nTetra[i][1] = nor.y;
        nTetra[i][2] = nor.z;
    }
	
    //Dibujo del Tetraedro
    for ( i=0; i<4; i++){
        glBegin( GL_TRIANGLES );
        glNormal3f( nTetra[i][0],nTetra[i][1],nTetra[i][2]);
        for(j=0; j<3; j++){
            px = spr.rs[cara[i][j]].px +  spr.rs[cara[i][j]].x1;
            py = spr.rs[cara[i][j]].py +  spr.rs[cara[i][j]].y1;
            pz = spr.rs[cara[i][j]].pz +  spr.rs[cara[i][j]].z1;
            glVertex3d( px, py, pz );
        }
        glEnd();
    }

Manejo del dispositivo Patriot

A continuación se muestra el código que permite controlar el puntero y el movimiento de los vértices con el dispositvo Patriot.

    char buf[100];
    ptracker->WriteTrkData((void *)"p",1);
    ptracker->ReadTrkData((void*)buf,100);
    pData=(float *)(buf + 8);
    pFlag = (int*)(buf + 68);
    int clAnt = click;
    XYZ mov_spr;

    click = pFlag[0];

    ang_x = pData[4];
    ang_y = pData[5];
    ang_z = pData[3];

    if(colVtx[nv] == true && click ==1 && !jala){
        ref.x = pData[0];
        ref.y = -pData[2];
        ref.z = pData[1];
        jala = true;
    }

    if(jala){
        mov_spr.x = pData[0];
        mov_spr.y = -pData[2];
        mov_spr.z = pData[1];
        if(norma(ref,mov_spr) < 20){
            spr.rs[nv].x = spr.rs[nv].px + 0.5*(mov_spr.x-ref.x);
            spr.rs[nv].y = spr.rs[nv].py + 0.5*(mov_spr.y-ref.y);
            spr.rs[nv].z = spr.rs[nv].pz + 0.5*(mov_spr.z-ref.z);
        }
    }

    if(clAnt==1 && click==0){
        jala = false;
    }

    if( clAnt==0 && click==1){
        control = true;
        referencia.x = pData[0];
        referencia.y = -pData[2];
        referencia.z = pData[1];
    }

    if(control && !jala){
        posPat[0] = pData[0]-referencia.x;
        posPat[1] = -pData[2]-referencia.y;
        posPat[2] = pData[1]-referencia.z;

        esf_cono.centro.x = pIn.x + posPat[0];
        esf_cono.centro.y = pIn.y + posPat[1];
        esf_cono.centro.z = pIn.z + posPat[2];
    }

Detección de Colisiones

Para la detección de colisiones se utilizaron las implementaciones de las tareas anteriores y el siguiente método:

    int i;
    colision = false;
    for(i=0; i<4; i++){
        colVtx[i] = false;
    }
    if (colisionMundo()){
        colision = true;
        esf_cono.centro = p;
    }else if( colisionConEsfera(&tet)){
            colision = true;
            esf_cono.centro = p;
    }else if( colisionTetra( )){
        colision = true;
        esf_cono.centro = p;
    }else if( colisionConEsfera( &esf_v1)){
            if( colisionConRayo( &esf_v1)){
                colVtx[0] = colision = true;
                nv = 0;
                esf_cono.centro = p;
			}
    }else if(colisionConEsfera( & esf_v2) ){
        if( colisionConRayo( &esf_v2)){
                colVtx[1] = colision = true;
                nv = 1;
                esf_cono.centro = p;
			}
    }else if(colisionConEsfera( & esf_v3) ){
        if( colisionConRayo( &esf_v3)){
                colVtx[2] = colision = true;
                nv = 2;
                esf_cono.centro = p;
			}
    }else if(colisionConEsfera( & esf_v4) ){
        if( colisionConRayo( &esf_v4)){
                colVtx[3] = colision = true;
                nv = 3;
                esf_cono.centro = p;
			}
    }

Deformación

La deformmación se implemeta con el siguuiente método, es actualizado según el timer, por lo que la ecuación diferencial es resuelta en todo momento.

		
        int i;
        MRA *pr;
		
        for( i=0; i<4; i++ ) {
        pr = &spr.rs[i];

        if ( i != nv ) {
           if ( i == 0 ) {
                pr->fx = (spr.rs[1].x1 + spr.rs[3].x1)*spr.k4;
                pr->fy = (spr.rs[1].y1 + spr.rs[3].y1)*spr.k4;
                pr->fz = (spr.rs[1].z1 + spr.rs[3].z1)*spr.k4;
            }
            else if ( i == 3 ) {
                pr->fx = (spr.rs[0].x1 + spr.rs[2].x1)*spr.k4;
                pr->fy = (spr.rs[0].y1 + spr.rs[2].y1)*spr.k4;
                pr->fz = (spr.rs[0].z1 + spr.rs[2].z1)*spr.k4;
            }
            else {
                pr->fx = (spr.rs[i-1].x1 + spr.rs[i+1].x1)*spr.k4;
                pr->fy = (spr.rs[i-1].y1 + spr.rs[i+1].y1)*spr.k4;
                pr->fz = (spr.rs[i-1].z1 + spr.rs[i+1].z1)*spr.k4;
            }
        }

        pr->x1 = spr.k1 * pr->x - spr.k2 * pr->x_1 + spr.k3 * pr->fx;
        pr->x_1 = pr->x;
        pr->x   = pr->x1;

        pr->y1 = spr.k1 * pr->y - spr.k2 * pr->y_1 + spr.k3 * pr->fy;
        pr->y_1 = pr->y;
        pr->y   = pr->y1;

        pr->z1 = spr.k1 * pr->z - spr.k2 * pr->z_1 + spr.k3 * pr->fz;
        pr->z_1 = pr->z;
        pr->z   = pr->z1;
    }

Los controles referidos al manejo del puntero, la iluminación, la posición de la cámara y la visión activa se mantienen igual que en las tareas anteriores.

Estos son los detalles a grandes rasgos de la implementación. Por último se agregan imágenes y el link de descarga del código fuente del programa:

vista1 vista2

vista3 vista3

vista3 vista3

Descargar Archivo


Bibliografía

[1] Mecate Zambrano Miriam. Interacción con Objetos Deformables. Tesis de maestría, CINVESTAV Departamento de Computación, México, D.F., Noviembre 2008.

[2] Alken, Inc. d/b/a Polhemus. PATRIOT USER MANUAL., Colchester, Vermont, U.S.A., Junio 2012.