OpenGL Game Tutorial
Visual C++ 6.0



Detekce kolizí kamery s BSP scénou


Příklad a zdroják ke stažení (3 837 k)

Princip detekce kolizí kamery s BSP scénou je celkem jednoduchý. V předešlém tutorialu na načítání Quake III BSP scény jsme se dozvěděli, že je scéna rozdělena na tzv. listy. Každý list obsahuje také informace o kolizích (brushes). Tyto informace již máme načteny, takže stačí procházet scénou a testovat, zda list, do kterého právě vcházíme obsahuje informace o kolizích. Pokud informace obsahuje, tak vrátíme kameru zpět na pozici před kolizí. Pokud list informace o kolizích neobsahuje, tak jím můžeme procházet.

Detekce kolize se neprovádí na bod pozice kamery, což by bylo nejjednodužší, ale vytvořil jsem kolem kamery detekční body. Pokud bychom prováděli detekci jen s bodem pozice kamery, nemohli bychom určit výšku a šířku avatara a navic by kamera byla tak blízko stěn, že by v některých případech docházelo k pohledu skrz stěnu.

Princip detekčních bodů si vysvětlíme na bodech přímky souřadnice X. Když kameru otočíme podle souřadnicového systému, tak vpravo od kamery je detekční bod +X, po levé straně detekční bod -X. Pokud dojde k detekci kolize bodu +X se stěnou, tak se kamera pohne v X směru zpět o hodnotu rychlosti kamery (KameraX = KameraX - RychlostX). Pokud dojde k detekci kolize bodu -X se stěnou, tak se kamera pohne v X směru zpět o hodnotu rychlosti kamery (KameraX = KameraX + RychlostX). Bod +X a -X tvoří přímku X. X přímky jsou tři. Horní a dolní, které určují výšku avatara a výšku předmětu, který nepude překročit. Třetí prostřední přímka protíná bod pozice kamery. Stejný algoritmus se provádí i pro souřadnici Z. U souřadnice Y jsou detekční přímky dvě, aby avatar nepropad skrz podlahu při přechodu z jednoho listu do druhého. Spodní body se generují tak, že v cyklu se odečítají hodnoty od výšky předmětu, který nejde překročit, po nejmenší hodnotu -Y, která určuje výšku avatara. Body se generují v cyklu aby avatar mohl chodit do schodů, případně překračovat objekty. Na obrázku jsou všechny tyto body a přímky názorně zobrazeny. Pozice kamery je uprostřed těchto přímek.

Detekční body kamery

Funkce BspCameraCollision(); vytvoří detekční body podle aktuální pozice kamery.


inline void K_CQuake3Bsp::BspCameraCollision()
{
        int i_dist = 30;              // Nastav detekční vzdálenost
        int i_ray_leaf_index;         // Index listu ve kterém se zrovna nachází bod aktuálního detekčního paprsku
        float pf_ray_point[3];        // XYZ pozice bodu aktuálního detekčního paprsku


        // X ray left down, point 1
        // Nastav levý dolní paprsek X,  bod 1
        pf_ray_point[0] = g_Camera.m_pfCurrPosition[0] - i_dist;
        pf_ray_point[1] = g_Camera.m_pfCurrPosition[1] - 15;
        pf_ray_point[2] = g_Camera.m_pfCurrPosition[2] - 10;
        if(g_bViewCameraCollisionPoints)        // Jestli to máš povolený, tak vykresli kolizní bod
        {
                glPushMatrix();                          // Začátek prostorové matice
                glPointSize(100.0f);                     // Nastav velikost bodu
                glBegin( GL_POINTS  );                   // Začni vykreslovat body
                        glVertex3fv( pf_ray_point );     // Zadej vrchol bodu
                glEnd();                                 // Ukonči vykreslování bodů
                glPopMatrix();                           // Konec prostorové matice
        }
        i_ray_leaf_index = FindLeaf(pf_ray_point, 0);        // Najdi list ve kterém se nachází kolizní bod
        if(CheckBrushCollision(i_ray_leaf_index))   // Jestliže došlo k detekci kolize
        {
                // Jestliže pohybuji kamerou doleva, nebo doprava, tak
                if(g_bCameraStrafeLeft || g_bCameraStrafeRight)
                {
                        // Musíš posunout X pozici kamery zpět o dnojnásobek hodnoty rychlosti
                        g_Camera.m_pfCurrPosition[0] += g_Camera.m_fSpeed * 2;
                }
                else        // Jinak
                {
                        // Posuň X pozici kamery zpět o hodnotu rychlosti
                        g_Camera.m_pfCurrPosition[0] += g_Camera.m_fSpeed;
                }

        }
        // X ray left down, point 2
        // Nastav levý dolní paprsek X,  bod 2
        // Kód je podobný, akorát je jiná pozice bodu a kamera se při detekci vrací opačným směrem
        pf_ray_point[0] = g_Camera.m_pfCurrPosition[0] + i_dist;
        pf_ray_point[1] = g_Camera.m_pfCurrPosition[1] - 15;
        pf_ray_point[2] = g_Camera.m_pfCurrPosition[2] - 10;
        if(g_bViewCameraCollisionPoints)
        {
                glPushMatrix();
                glPointSize(100.0f);
                glBegin( GL_POINTS  );
                        glVertex3fv( pf_ray_point );
                glEnd();
                glPopMatrix();
        }
        i_ray_leaf_index = FindLeaf(pf_ray_point, 0);
        if(CheckBrushCollision(i_ray_leaf_index))
        {
                if(g_bCameraStrafeLeft || g_bCameraStrafeRight)
                {
                        g_Camera.m_pfCurrPosition[0] -= g_Camera.m_fSpeed * 2;
                }
                else
                {
                        g_Camera.m_pfCurrPosition[0] -= g_Camera.m_fSpeed;
                }
        }

        // X ray middle, point 1
        // Nastav prostřední paprsek X,  bod 1
        pf_ray_point[0] = g_Camera.m_pfCurrPosition[0] - i_dist;
        pf_ray_point[1] = g_Camera.m_pfCurrPosition[1];
        pf_ray_point[2] = g_Camera.m_pfCurrPosition[2];
        if(g_bViewCameraCollisionPoints)
        {
                glPushMatrix();
                glPointSize(100.0f);
                glBegin( GL_POINTS  );
                        glVertex3fv( pf_ray_point );
                glEnd();
                glPopMatrix();
        }
        i_ray_leaf_index = FindLeaf(pf_ray_point, 0);
        if(CheckBrushCollision(i_ray_leaf_index))
        {
                if(g_bCameraStrafeLeft || g_bCameraStrafeRight)
                {
                        g_Camera.m_pfCurrPosition[0] += g_Camera.m_fSpeed * 2;
                }
                else
                {
                        g_Camera.m_pfCurrPosition[0] += g_Camera.m_fSpeed;
                }
        }
        // X ray middle, point 2
        // Nastav prostřední paprsek X,  bod 2
        pf_ray_point[0] = g_Camera.m_pfCurrPosition[0] + i_dist;
        pf_ray_point[1] = g_Camera.m_pfCurrPosition[1];
        pf_ray_point[2] = g_Camera.m_pfCurrPosition[2];
        if(g_bViewCameraCollisionPoints)
        {
                glPushMatrix();
                glPointSize(100.0f);
                glBegin( GL_POINTS  );
                        glVertex3fv( pf_ray_point );
                glEnd();
                glPopMatrix();
        }
        i_ray_leaf_index = FindLeaf(pf_ray_point, 0);
        if(CheckBrushCollision(i_ray_leaf_index))
        {
                if(g_bCameraStrafeLeft || g_bCameraStrafeRight)
                {
                        g_Camera.m_pfCurrPosition[0] -= g_Camera.m_fSpeed * 2;
                }
                else
                {
                        g_Camera.m_pfCurrPosition[0] -= g_Camera.m_fSpeed;
                }
        }

        // X ray right up, point 1
        // Nastav pravý horní paprsek X,  bod 1
        pf_ray_point[0] = g_Camera.m_pfCurrPosition[0] - i_dist;
        pf_ray_point[1] = g_Camera.m_pfCurrPosition[1] + 15;
        pf_ray_point[2] = g_Camera.m_pfCurrPosition[2] + 10;
        if(g_bViewCameraCollisionPoints)
        {
                glPushMatrix();
                glPointSize(100.0f);
                glBegin( GL_POINTS  );
                        glVertex3fv( pf_ray_point );
                glEnd();
                glPopMatrix();
        }
        i_ray_leaf_index = FindLeaf(pf_ray_point, 0);
        if(CheckBrushCollision(i_ray_leaf_index))
        {
                if(g_bCameraStrafeLeft || g_bCameraStrafeRight)
                {
                        g_Camera.m_pfCurrPosition[0] += g_Camera.m_fSpeed * 2;
                }
                else
                {
                        g_Camera.m_pfCurrPosition[0] += g_Camera.m_fSpeed;
                }
        }
        // X ray right up, point 2
        // Nastav pravý horní paprsek X,  bod 2
        pf_ray_point[0] = g_Camera.m_pfCurrPosition[0] + i_dist;
        pf_ray_point[1] = g_Camera.m_pfCurrPosition[1] + 15;
        pf_ray_point[2] = g_Camera.m_pfCurrPosition[2] + 10;
        if(g_bViewCameraCollisionPoints)
        {
                glPushMatrix();
                glPointSize(100.0f);
                glBegin( GL_POINTS  );
                        glVertex3fv( pf_ray_point );
                glEnd();
                glPopMatrix();
        }
        i_ray_leaf_index = FindLeaf(pf_ray_point, 0);
        if(CheckBrushCollision(i_ray_leaf_index))
        {
                if(g_bCameraStrafeLeft || g_bCameraStrafeRight)
                {
                        g_Camera.m_pfCurrPosition[0] -= g_Camera.m_fSpeed * 2;
                }
                else
                {
                        g_Camera.m_pfCurrPosition[0] -= g_Camera.m_fSpeed;
                }
        }

        //////////////////////////////////


        // Y ray right backward, point 1
        // Nastav pravý zadní paprsek Y, bod 1
        pf_ray_point[0] = g_Camera.m_pfCurrPosition[0] + 2;
        pf_ray_point[1] = g_Camera.m_pfCurrPosition[1] - 50;
        pf_ray_point[2] = g_Camera.m_pfCurrPosition[2] + 2;
        if(g_bViewCameraCollisionPoints)
        {
                glPushMatrix();
                glPointSize(100.0f);
                glBegin( GL_POINTS  );
                        glVertex3fv( pf_ray_point );
                glEnd();
                glPopMatrix();
        }
        // Cykluj, pokud je bod větší než jemenší detekční hodnota
        // Cyklus se provádí aby kamera chodila do schodů
        while(pf_ray_point[1] > g_Camera.m_pfCurrPosition[1] - 70)
        {
                        pf_ray_point[1]--;                                       // Odečti hodnotu bodu
                        int i_ray_leaf_index = FindLeaf(pf_ray_point, 0);        // Najdi list ve kterém se nachází kolizní bod
                        if(CheckBrushCollision(i_ray_leaf_index))                // Jestliže došlo k detekci kolize
                        {
                                g_Camera.GoUp();                                 // Tak pohni kamerou nahoru
                                break;                                           // A vyskoč z cyklu
                        }
        }
        // Y ray right forward, point 1
        // Nastav pravý přední paprsek Y, bod 1
        // Opakuje se předchozí algoritmus
        pf_ray_point[0] = g_Camera.m_pfCurrPosition[0] + 2;
        pf_ray_point[1] = g_Camera.m_pfCurrPosition[1] - 50;
        pf_ray_point[2] = g_Camera.m_pfCurrPosition[2] - 2;
        if(g_bViewCameraCollisionPoints)
        {
                glPushMatrix();
                glPointSize(100.0f);
                glBegin( GL_POINTS  );
                        glVertex3fv( pf_ray_point );
                glEnd();
                glPopMatrix();
        }
        while(pf_ray_point[1] > g_Camera.m_pfCurrPosition[1] - 70)
        {
                        pf_ray_point[1]--;
                        int i_ray_leaf_index = FindLeaf(pf_ray_point, 0);
                        if(CheckBrushCollision(i_ray_leaf_index))
                        {
                                g_Camera.GoUp();
                                break;
                        }
        }

        // Y ray right backward, point 2
        // Nastav pravý zadní paprsek Y, bod 2
        pf_ray_point[0] = g_Camera.m_pfCurrPosition[0] + 2;
        pf_ray_point[1] = g_Camera.m_pfCurrPosition[1] + 20;
        pf_ray_point[2] = g_Camera.m_pfCurrPosition[2] + 2;
        if(g_bViewCameraCollisionPoints)
        {
                glPushMatrix();
                glPointSize(100.0f);
                glBegin( GL_POINTS  );
                        glVertex3fv( pf_ray_point );
                glEnd();
                glPopMatrix();
        }
        i_ray_leaf_index = FindLeaf(pf_ray_point, 0);
        if(CheckBrushCollision(i_ray_leaf_index))
        {
                g_Camera.GoDown();                // Posuň kameru dolu
        }
        // Y ray right forward, point 2
        // Nastav pravý přední paprsek Y, bod 2
        pf_ray_point[0] = g_Camera.m_pfCurrPosition[0] + 2;
        pf_ray_point[1] = g_Camera.m_pfCurrPosition[1] + 20;
        pf_ray_point[2] = g_Camera.m_pfCurrPosition[2] - 2;
        if(g_bViewCameraCollisionPoints)
        {
                glPushMatrix();
                glPointSize(100.0f);
                glBegin( GL_POINTS  );
                        glVertex3fv( pf_ray_point );
                glEnd();
                glPopMatrix();
        }
        i_ray_leaf_index = FindLeaf(pf_ray_point, 0);
        if(CheckBrushCollision(i_ray_leaf_index))
        {
                g_Camera.GoDown();
        }


        /////////////////////////////////


        // Z ray left down, point 1
        // Nastav levý dolní paprsek Z,  bod 1
        // Algoritmus stejný jako s osou X, akorát se kamera vrací v Z ose
        pf_ray_point[0] = g_Camera.m_pfCurrPosition[0] - 10;
        pf_ray_point[1] = g_Camera.m_pfCurrPosition[1] - 15;
        pf_ray_point[2] = g_Camera.m_pfCurrPosition[2] - i_dist;
        if(g_bViewCameraCollisionPoints)
        {
                glPushMatrix();
                glPointSize(100.0f);
                glBegin( GL_POINTS  );
                        glVertex3fv( pf_ray_point );
                glEnd();
                glPopMatrix();
        }
        i_ray_leaf_index = FindLeaf(pf_ray_point, 0);
        if(CheckBrushCollision(i_ray_leaf_index))
        {
                if(g_bCameraStrafeLeft || g_bCameraStrafeRight)
                {
                        g_Camera.m_pfCurrPosition[2] += g_Camera.m_fSpeed * 2;
                }
                else
                {
                        g_Camera.m_pfCurrPosition[2] += g_Camera.m_fSpeed;
                }
        }
        // Z ray left down, point 2
        // Nastav levý dolní paprsek Z,  bod 2
        pf_ray_point[0] = g_Camera.m_pfCurrPosition[0] - 10;
        pf_ray_point[1] = g_Camera.m_pfCurrPosition[1] - 15;
        pf_ray_point[2] = g_Camera.m_pfCurrPosition[2] + i_dist;
        if(g_bViewCameraCollisionPoints)
        {
                glPushMatrix();
                glPointSize(100.0f);
                glBegin( GL_POINTS  );
                        glVertex3fv( pf_ray_point );
                glEnd();
                glPopMatrix();
        }
        i_ray_leaf_index = FindLeaf(pf_ray_point, 0);
        if(CheckBrushCollision(i_ray_leaf_index))
        {
                if(g_bCameraStrafeLeft || g_bCameraStrafeRight)
                {
                        g_Camera.m_pfCurrPosition[2] -= g_Camera.m_fSpeed * 2;
                }
                else
                {
                        g_Camera.m_pfCurrPosition[2] -= g_Camera.m_fSpeed;
                }
        }

        // Z ray middle, point 1
        // Nastav prostřední paprsek Z,  bod 1
        pf_ray_point[0] = g_Camera.m_pfCurrPosition[0];
        pf_ray_point[1] = g_Camera.m_pfCurrPosition[1];
        pf_ray_point[2] = g_Camera.m_pfCurrPosition[2] - i_dist;

        if(g_bViewCameraCollisionPoints)
        {
                glPushMatrix();
                glPointSize(100.0f);
                glBegin( GL_POINTS  );
                        glVertex3fv( pf_ray_point );
                glEnd();
                glPopMatrix();
        }

        i_ray_leaf_index = FindLeaf(pf_ray_point, 0);
        if(CheckBrushCollision(i_ray_leaf_index))
        {
                if(g_bCameraStrafeLeft || g_bCameraStrafeRight)
                {
                        g_Camera.m_pfCurrPosition[2] += g_Camera.m_fSpeed * 2;
                }
                else
                {
                        g_Camera.m_pfCurrPosition[2] += g_Camera.m_fSpeed;
                }
        }
        // Z ray middle, point 2
        // Nastav prostřední paprsek Z,  bod 2
        pf_ray_point[0] = g_Camera.m_pfCurrPosition[0];
        pf_ray_point[1] = g_Camera.m_pfCurrPosition[1];
        pf_ray_point[2] = g_Camera.m_pfCurrPosition[2] + i_dist;

        if(g_bViewCameraCollisionPoints)
        {
                glPushMatrix();
                glPointSize(100.0f);
                glBegin( GL_POINTS  );
                        glVertex3fv( pf_ray_point );
                glEnd();
                glPopMatrix();
        }
        i_ray_leaf_index = FindLeaf(pf_ray_point, 0);
        if(CheckBrushCollision(i_ray_leaf_index))
        {
                if(g_bCameraStrafeLeft || g_bCameraStrafeRight)
                {
                        g_Camera.m_pfCurrPosition[2] -= g_Camera.m_fSpeed * 2;
                }
                else
                {
                        g_Camera.m_pfCurrPosition[2] -= g_Camera.m_fSpeed;
                }
        }

        // Z ray right up, point 1
        // Nastav pravý horní paprsek Z,  bod 1
        pf_ray_point[0] = g_Camera.m_pfCurrPosition[0] + 10;
        pf_ray_point[1] = g_Camera.m_pfCurrPosition[1] + 15;
        pf_ray_point[2] = g_Camera.m_pfCurrPosition[2] - i_dist;
        if(g_bViewCameraCollisionPoints)
        {
                glPushMatrix();
                glPointSize(100.0f);
                glBegin( GL_POINTS  );
                        glVertex3fv( pf_ray_point );
                glEnd();
                glPopMatrix();
        }
        i_ray_leaf_index = FindLeaf(pf_ray_point, 0);
        if(CheckBrushCollision(i_ray_leaf_index))
        {
                if(g_bCameraStrafeLeft || g_bCameraStrafeRight)
                {
                        g_Camera.m_pfCurrPosition[2] += g_Camera.m_fSpeed * 2;
                }
                else
                {
                        g_Camera.m_pfCurrPosition[2] += g_Camera.m_fSpeed;
                }
        }
        // Z ray right up, point 2
        // Nastav pravý horní paprsek Z,  bod 2
        pf_ray_point[0] = g_Camera.m_pfCurrPosition[0] + 10;
        pf_ray_point[1] = g_Camera.m_pfCurrPosition[1] + 15;
        pf_ray_point[2] = g_Camera.m_pfCurrPosition[2] + i_dist;
        if(g_bViewCameraCollisionPoints)
        {
                glPushMatrix();
                glPointSize(100.0f);
                glBegin( GL_POINTS  );
                        glVertex3fv( pf_ray_point );
                glEnd();
                glPopMatrix();
        }
        i_ray_leaf_index = FindLeaf(pf_ray_point, 0);
        if(CheckBrushCollision(i_ray_leaf_index))
        {
                if(g_bCameraStrafeLeft || g_bCameraStrafeRight)
                {
                        g_Camera.m_pfCurrPosition[2] -= g_Camera.m_fSpeed * 2;
                }
                else
                {
                        g_Camera.m_pfCurrPosition[2] -= g_Camera.m_fSpeed;
                }
        }


}


Pro každý bod se volá funkce CheckBrushCollision(i_ray_leaf_index) která zjistí, zda došlo ke kolizi bodu s BSP listem, který obsahuje brush informace.

// Zjisti potřebné informace pro kolize kamery se stěnama scény
inline bool K_CQuake3Bsp::CheckBrushCollision(int i_leaf_index)
{
     // Pokud se detekuje kolize s těmito listy, tak vrať funkci false
     if((i_leaf_index == 440) || (i_leaf_index == 1609) || (i_leaf_index == 255) || (i_leaf_index == 197)
                              || (i_leaf_index == 1395) || (i_leaf_index == 1474) || (i_leaf_index == 1035) )
     {
          return false;
     }

     // Jinak

     TBspLeaf *pt_bsp_leaf = &(m_ptBspLeaf[i_leaf_index]);            // Získej hodnoty aktuálního listu
     int i_num_leaf_brushes = pt_bsp_leaf->i_num_of_leaf_brushes;     // Získej počet kolizí

     // Jestliže list obsahuje kolize
     if(i_num_leaf_brushes)
     {
          // Jestliže je povoleno vykreslování detekční obálky kolizního listu ve které je kamera, tak
          if (g_bRenderBoundingBoxCameraLeafColl)
          {
               // Vykresli obálky
               RenderBoundingBox(m_ptBspLeaf[i_leaf_index].pi_min_box, m_ptBspLeaf[i_leaf_index].pi_max_box);
          }
          return true;
     }
     else     // Pokud nedochází k detekci kolize
     {
          return false;
     }

}

Toť vše. Příště zkusíme načíst QuakeIII animované postavy MD3 a potom se vrhneme na AI těchto postav.




Home