OpenGL v Delphi4



Kolize koule a kamery se stěnama místnosti.


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


Trochu jsem zdokonalil předešlý tutorial na detekci kolizí. Vytvořil jsem jednoduchou místnost se sloupem. V místnosti skáče koule a odráží se od stěn. Přidal jsem i detekci kolize kamery se stěnama. Aplikace běží v odděleném procesu 'Thread', který jsem trochu vylepšil. Procedury aktivující 'Thread' jsou obsaženy v unitě RenderThread

Algoritmus pro detekci kolizí je stejný. Všechny funkce jsou v unitě K3dMath.pas . Do této unity jsem jen připsal algoritmus na normalizaci vektoru, kterou budu potřebovat při výpočtu kolize koule se stěnama.

V předešlém tutorialu kolidovala čára s jedním trojúhelníkem. Kolize však funguje vlastně jen v jedné souřadnici. Já však potřebuji, aby kolize fungovaly ve všech souřadnicích, proto jsem vytvořil tři čáry 'LineX, LineY, LineZ'.

Pokud chci, aby kolidovala koule, místo čar vykreslím kouli. Pokud chci, aby kolidovala kamera, přepočítám pozici čar tak, aby souřadnice průsečíku čar a pozice kamery byly totožné. Čáry opět nevykresluji.

Kolizi koule počítám takto:
Nejdříve vypočítám, kolizi všech tří čar.


  //LineX
  bCollidedLineX := IntersectedPolygon(Point1[val], Point2[val], Point3[val] , pLineX1, pLineX2);
  if bCollidedLineX
  then begin
    LineX := True;
  end;
  //LineY
  bCollidedLineY := IntersectedPolygon(Point1[val], Point2[val], Point3[val] , pLineY1, pLineY2);
  if bCollidedLineY
  then begin
    LineY := True;
  end;
  //LineZ
  bCollidedLineZ := IntersectedPolygon(Point1[val], Point2[val], Point3[val] , pLineZ1, pLineZ2);
  if bCollidedLineZ
  then begin
    LineZ := True;
  end;

Pokud koliduje alespoň jedna z čar.

  if LineX or LineY or LineZ then begin
    Coll := True;
  end else begin
    Coll := False;
  end;

Změním vektor pohybu koule.

 if Coll then begin
    if FirstColl then begin
      FirstColl := False;
      TriVector := CalcCollVector(Point1[val], Point2[val], Point3[val]);
      NewVector.x := (LineVector.x - TriVector.x);
      NewVector.y := (LineVector.y - TriVector.y);
      NewVector.z := (LineVector.z - TriVector.z);
      LineVector := NormalizeVector(NewVector);
      MoveSpeedLine := 0.06;
    end; //konec FirstColl
  end else begin
    FirstColl := True;
    MoveSpeedLine := 0.05;
  end;

Proměnnou 'FirstColl' tam mám proto, aby se kolize počítala jen při prvním střetu čáry se stěnou. Změní se vektor pohybu čar (koule) a pokud čáry neopustí prostor stěny, kolize se dále nepočítají.

Výpočet funguje takto:
Nejdříve se vypočítá vektor stěny (trojúhelníka)

 
  TriVector := CalcCollVector(Point1[val], Point2[val], Point3[val]);
a potom nový vektor odečtením vektoru stěny od stávajícího vektoru pohybu čar.
 
  NewVector.x := (LineVector.x - TriVector.x);
  NewVector.y := (LineVector.y - TriVector.y);
  NewVector.z := (LineVector.z - TriVector.z);
Nakonec převedu nový vektor do vektoru pohybu čar.
 
  LineVector := NormalizeVector(NewVector);

Pokud počítám kolizi kamery se stěnama, tak si nejdříve vypočítám průsečík čar, aby jeho souřadnice byly totožné s pozicí kamery.


   //Line X
   LineX1.x :=  Tran.X - 2;
   LineX1.y :=  Tran.Y;
   LineX1.z :=  Tran.Z;
   LineX2.x :=  Tran.X + 2;
   LineX2.y :=  Tran.Y;
   LineX2.z :=  Tran.Z;

   //Line Y
   LineY1.x :=  Tran.X;
   LineY1.y :=  Tran.Y - 2;
   LineY1.z :=  Tran.Z;
   LineY2.x :=  Tran.X;
   LineY2.y :=  Tran.Y + 2;
   LineY2.z :=  Tran.Z;

   //Line Z
   LineZ1.x :=  Tran.X;
   LineZ1.y :=  Tran.Y;
   LineZ1.z :=  Tran.Z - 2;
   LineZ2.x :=  Tran.X;
   LineZ2.y :=  Tran.Y;
   LineZ2.z :=  Tran.Z + 2;

Výpočet kolize čar je stejný jako u koule

  //LineX
  bCollidedLineX := IntersectedPolygon(Point1[val], Point2[val], Point3[val] , LineX1, LineX2);
  if bCollidedLineX
  then begin
    bLineX := True;
  end;
  //LineY
  bCollidedLineY := IntersectedPolygon(Point1[val], Point2[val], Point3[val] , LineY1, LineY2);
  if bCollidedLineY
  then begin
    bLineY := True;
  end;
  //LineZ
  bCollidedLineZ := IntersectedPolygon(Point1[val], Point2[val], Point3[val] , LineZ1, LineZ2);
  if bCollidedLineZ
  then begin
    bLineZ := True;
  end;                    

Pokud koliduje alespoň jedna z čar.

  if bLineX or bLineY or bLineZ then begin
    Coll := True;
  end else begin
    Coll := False;
  end;

Vrátím kameru zpět ve směru vektoru kolizní stěny.

 if Coll then begin
   TriVector := CalcCollVector(Point1[val], Point2[val], Point3[val]);
   Tran.x := Tran.x - (TriVector.x * MoveSpeed);
   Tran.y := Tran.y - (TriVector.y * MoveSpeed);
   Tran.z := Tran.z - (TriVector.z * MoveSpeed);
 end;

A už to funguje.

Kouli vytvořím voláním procedury 'DrawSphere'


procedure DrawSphere;
var
  qObj: PGLUquadricObj;
  X,Y,Z: TGLFloat;
begin
  if Sphere then begin
    glEnable(GL_LIGHTING);
    X := pLineZ1.x;
    Y := pLineZ1.y;
    Z := pLineZ1.z + 2;

    glPushMatrix;
      glTranslatef(X,Y,Z);                                //pozice kamery
      glColor3f(1, 1, 1);
      glBindTexture(GL_TEXTURE_2D, TextureBin[21]);
      glEnable(GL_TEXTURE_2D);
      qobj := gluNewQuadric();                    //vytvori novy objekt
      gluQuadricTexture(qobj,GL_true);            //otexturuje
      gluQuadricDrawStyle(qobj, GLU_FILL);        //typ zobrazeni
      gluQuadricOrientation(qobj,GLU_OUTSIDE);    //vnejsi strana odrazi svetlo
      gluQuadricNormals(qobj, GLU_SMOOTH);        //volba stinováni
      gluSphere(qobj, 2, 20, 20);              //zobrazi kouli s polomerem 2 a siti 20x20 
    glPopMatrix;
  end;
end;       

Koule se pohybuje podle pozice čar, které definuji v proceduře 'DrawLine'

procedure DrawLine;
begin
  glDisable(GL_TEXTURE_2D);
  glDisable(GL_LIGHTING);
  if LineZ or LineX or LineY then begin
    glColor3f(1, 0, 0);                        // Make the line RED if we collided with the triangle's plane
  end else begin
    glColor3f(1, 1, 0);                  // Make the line YELLOW if we didn't collide
  end;
  LineZ := False;
  LineX := False;
  LineY := False;

  //Move collision lines
  pLineX1.x := pLineX1.x + (LineVector.x * MoveSpeedLine);
  pLineX1.y := pLineX1.y + (LineVector.y * MoveSpeedLine);
  pLineX1.z := pLineX1.z + (LineVector.z * MoveSpeedLine);
  pLineX2.x := pLineX2.x + (LineVector.x * MoveSpeedLine);
  pLineX2.y := pLineX2.y + (LineVector.y * MoveSpeedLine);
  pLineX2.z := pLineX2.z + (LineVector.z * MoveSpeedLine);

  pLineY1.x := pLineY1.x + (LineVector.x * MoveSpeedLine);
  pLineY1.y := pLineY1.y + (LineVector.y * MoveSpeedLine);
  pLineY1.z := pLineY1.z + (LineVector.z * MoveSpeedLine);
  pLineY2.x := pLineY2.x + (LineVector.x * MoveSpeedLine);
  pLineY2.y := pLineY2.y + (LineVector.y * MoveSpeedLine);
  pLineY2.z := pLineY2.z + (LineVector.z * MoveSpeedLine);

  pLineZ1.x := pLineZ1.x + (LineVector.x * MoveSpeedLine);
  pLineZ1.y := pLineZ1.y + (LineVector.y * MoveSpeedLine);
  pLineZ1.z := pLineZ1.z + (LineVector.z * MoveSpeedLine);
  pLineZ2.x := pLineZ2.x + (LineVector.x * MoveSpeedLine);
  pLineZ2.y := pLineZ2.y + (LineVector.y * MoveSpeedLine);
  pLineZ2.z := pLineZ2.z + (LineVector.z * MoveSpeedLine);

  if Lines then begin
  glPushMatrix;
  glBegin(GL_LINES);                                        // This is our BEGIN to draw LineX
    glVertex3f(pLineX1.x, pLineX1.y, pLineX1.z);
    glVertex3f(pLineX2.x, pLineX2.y, pLineX2.z);
  glEnd;
  glBegin(GL_LINES);                                        // This is our BEGIN to draw Line Y
    glVertex3f(pLineY1.x, pLineY1.y, pLineY1.z);
    glVertex3f(pLineY2.x, pLineY2.y, pLineY2.z);
  glEnd;
  glBegin(GL_LINES);                                        // This is our BEGIN to draw LineZ
    glVertex3f(pLineZ1.x, pLineZ1.y, pLineZ1.z);
    glVertex3f(pLineZ2.x, pLineZ2.y, pLineZ2.z);
  glEnd;
  glPopMatrix;
  end;
end;

Tlačítkem 'L' zobrazím místo koule čáry, tlačítkem 'S' zobrazím kouli. Kamera se pohybuje prostorem pomocí šipek, pokud zmáčknu mezerník a šipku doleva, nebo doprava, kamera se neotáčí, ale posouvá. Tlačítky 'PageUp, PageDown' kamera lítá nahoru dolu. Tlačítky 'Home, End' kouká kamera nahoru dolu.

Celé zdrojáky jsou tady
BaColl_f.pas
K3dMath_f.pas
BaColl.dpr


Lepší detekce kolizí

Home