Aktuelle Zeit: 22.12.2024, 22:01

Alle Zeiten sind UTC + 1 Stunde




Ein neues Thema erstellen Auf das Thema antworten  [ 1 Beitrag ] 
Autor Nachricht
BeitragVerfasst: 08.04.2007, 20:32 
Offline
Benutzeravatar

Registriert: 13.03.2007, 11:37
Beiträge: 7
Wohnort: Erfurt
Tutorial 7: Collision Detection and Response (Kollisionserfassung und /-reaktion)

(übersetzt von Ba'el, orig. eng. von irrlicht.sourceforge.net)
In diesem Tutorial wird gezeigt wie man Kollision, mit der Irrlicht Engine, feststellen kann. Es werden 3 Methoden beschrieben: Automatic collision detection für das Bewegen durch 3D-Welten (gleiten, Stufen steigen und rutschen) manuell Dreiecke auswählen und manuell Szeneknoten auswählen.
Das Programm wird so aussehen:

Bild

Los geht's!
Zu erst nehmen wir das Programm aus Tutorial 2, welches ein Quake 3 Level lädt und darstellt. Wir nutzen das Level um darin zu gehen und davon Dreiecke auszuwählen. Dazu fügen wir 3 animierte Modele hinzu um Szeneknoten auszuwählen. Der folgende Code wird nicht weiter erläutert, da dies schon im Tutorial 2 geschah.
Code:
#include <irrlicht.h>
#include <iostream>

using namespace irr;

#pragma comment(lib, "Irrlicht.lib")

int main()
{
  // let user select driver type

  video::E_DRIVER_TYPE driverType;

printf("Please select the driver you want for this example:\n"\
    " (a) Direct3D 9.0c\n (b) Direct3D 8.1\n (c) OpenGL 1.5\n"\
    " (d) Software Renderer\n (e) Apfelbaum Software Renderer\n"\
    " (f) NullDevice\n (otherKey) exit\n\n");

  char i;
  std::cin >> i;

  switch(i)
  {
  case 'a': driverType = video::EDT_DIRECT3D9;break;
  case 'b': driverType = video::EDT_DIRECT3D8;break;
  case 'c': driverType = video::EDT_OPENGL;   break;
  case 'd': driverType = video::EDT_SOFTWARE; break;
  case 'e': driverType = video::EDT_SOFTWARE2;break; 
  case 'f': driverType = video::EDT_NULL;     break;
  default: return 0;
  }   

  // create device
  IrrlichtDevice *device = createDevice(driverType,
     core::dimension2d<s32>(640, 480), 16, false);

  if (device == 0)
    return 1; // could not create selected driver.

  video::IVideoDriver* driver = device->getVideoDriver();
  scene::ISceneManager* smgr = device->getSceneManager();

  device->getFileSystem()->addZipFileArchive
      ("../../media/map-20kdm2.pk3");

   
   scene::IAnimatedMesh* q3levelmesh = smgr->getMesh("20kdm2.bsp");
   scene::ISceneNode* q3node = 0;
   
   if (q3levelmesh)
      q3node = smgr->addOctTreeSceneNode(q3levelmesh->getMesh(0));
So weit so gut wir laden das Quake 3 Level wie in Tutorial 2. Jetzt kommt einiges was anders ist: Wir erstellen einen Dreiecksauswähler (triangle selector). Ein Dreiecksauswähler ist eine Klasse welche sich Dreiecke von Szenenknoten holen kann, um damit verschiedene Dinge zu tun, z.B. Kollisionserfassung. Es gibt verschiedene Dreiecksauswähler, und alle können mit dem ISceneManager erstellt werden. In diesem Beispiel erstellen wir einen OctTreeTriangleSelector, welcher die Dreiecksausgabe ein wenig optimiert durch Reduzierung dieser wie einen „octree“. Dies ist sehr nützlich bei enorm großen Meshes wie Quake 3 Levels.
Danach erstellen wir den Dreieckauswähler und knüpfen in an die q3node. Dies ist nicht notwendig, aber in diesem Fall müssen wir uns nicht um den Auswähler sorgen, z.B. müssen wir nach dem droppen nix weiter tun.
Code:
scene::ITriangleSelector* selector = 0;
   
   if (q3node)
   {      
      q3node->setPosition(core::vector3df(-1370,-130,-1400));

      selector = smgr->createOctTreeTriangleSelector(
            q3levelmesh->getMesh(0), q3node, 128);
      q3node->setTriangleSelector(selector);
      selector->drop();
   }
Wir fügen eine “first person shooter camera” in die Szene ein um uns im Quake 3 Level, wie im Tutorial 2, bewegen zu können. Aber diesmal fügen wir noch einen Spezialanimator zu der Kamera: Einen Collision Response animator (Animator der Reaktion auf Kollision). Wenn dieses Ding mit dem Szeneknoten verknüpft ist wird dieser so modifiziert das man nicht mehr durch Wände gehen kann und auch der Gravitation unterliegt. Das einzige was wir dem Animator mitteilen müssen ist wie die Welt aussieht, wie groß der Szeneknoten ist, welche Gravitation wirkt und so weiter. Nachdem wir den “collision response animator” zur Kamera gefügt haben müssen wir für die Kollisionsfeststellung nix mehr tun, der Rest funktioniert automatisch. Und merk dir noch andere coole Möglichkeiten: Der „collsion response animator“ kann auch an alle anderen Szeneknoten angefügt werden, nich nur an Kameras. Und er kann auch mit anderen Szeneknotenanimatoren gemixt werden. Wie in diesem Fall bei Kollisionserfassung und /-reagierung, in der Irrlicht Engine ist wirklich sehr sehr einfach.
Nun wollen wir noch einen abschliesenden Blick auf die Parameter von createCollisionResponseAnimator() werfen. Der erste Parameter ist der TriangleSelector (Dreieckauswähler), welcher beschreibt wie die Welt, nachdem die Kollisionserfassung fertig ist, aussieht. Der zweite Parameter ist der Szeneknoten, dieser ist das Objekt, welches von der Kollisionserfassung beeinflusst wird, in diesem Beispiel ist es die Kamera. Der dritte definiert wie groß das Objekt ist, es ist der Radius eines Ellipsoids. Probier es aus und verkleinere den Wert des Radius , die Kamera danach mit der Wand auf Tuchfühlung gehen. Der nächste Parameter ist die Geschwindigkeit und Ausrichtung der Gravitation. Du kannst ihn auf (0,0,0) setzen um die Gravitation zu deaktivieren. Und der letzte Wert ist einfach die Translation: Ohne diesen ist der Ellipsoid, nachdem die Kollisionserkennung beendet ist, wird um die Kamera herum sein, und die Kamera wird in der Mitte des Ellipsoiden sein. Aber da wir als Menschen unsere Augen vorn am Körper haben, mit welchen wir mit unserer Welt zusammenprallen, und nicht in der Mitte von diesem. Also platzieren wir den Szeneknoten 50 Einheiten über dem Zentrum des Ellipsoiden durch diesen Parameter. Das war's, die Kollisionserfassung funkt.
Code:
   scene::ICameraSceneNode* camera =    
      camera = smgr->addCameraSceneNodeFPS(0,100.0f,300.0f);
   camera->setPosition(core::vector3df(-100,50,-150));

   scene::ISceneNodeAnimator* anim =
    smgr->createCollisionResponseAnimator(
      selector, camera, core::vector3df(30,50,30),
      core::vector3df(0,-3,0),
      core::vector3df(0,50,0));

   camera->addAnimator(anim);
   anim->drop();
Aber Kollisionserfassung ist in Irrlicht keine große Sache. Im nächsten Abschnitt beschreibe ich zwei verschiedene Arten. Aber zuvor werden wir die Szene ein wenig präperieren. Wir benötigen drei animierte Charaktere welche wir später auswählen können, ein dynamisches Licht um sie auszuleuchten, eine Wand zum zeichnen wo wir einen Knotenpunkt finden, und ja, wir müssen den Maus-cursor loswerden. :)
Code:
   // disable mouse cursor

   device->getCursorControl()->setVisible(false);

   // add billboard

   scene::IBillboardSceneNode * bill = smgr->addBillboardSceneNode();
   bill->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR );
   bill->setMaterialTexture(0, driver->getTexture(
         "../../media/particle.bmp"));
   bill->setMaterialFlag(video::EMF_LIGHTING, false);
   bill->setSize(core::dimension2d<f32>(20.0f, 20.0f));

   // add 3 animated faeries.

   video::SMaterial material;
   material.Texture1 = driver->getTexture(
         "../../media/faerie2.bmp");
   material.Lighting = true;

   scene::IAnimatedMeshSceneNode* node = 0;
   scene::IAnimatedMesh* faerie = smgr->getMesh(
        "../../media/faerie.md2");

   if (faerie)
   {
      node = smgr->addAnimatedMeshSceneNode(faerie);
      node->setPosition(core::vector3df(-70,0,-90));
      node->setMD2Animation(scene::EMAT_RUN);
      node->getMaterial(0) = material;

      node = smgr->addAnimatedMeshSceneNode(faerie);
      node->setPosition(core::vector3df(-70,0,-30));
      node->setMD2Animation(scene::EMAT_SALUTE);
      node->getMaterial(0) = material;

      node = smgr->addAnimatedMeshSceneNode(faerie);
      node->setPosition(core::vector3df(-70,0,-60));
      node->setMD2Animation(scene::EMAT_JUMP);
      node->getMaterial(0) = material;
   }

   material.Texture1 = 0;
   material.Lighting = false;

   // Add a light

   smgr->addLightSceneNode(0, core::vector3df(-60,100,400),
      video::SColorf(1.0f,1.0f,1.0f,1.0f),
      600.0f);
Um es nicht unnötig zu verkomplizieren, packen wir es in die zeichnende Schleife. Wir nehmen zwei Pointer um den aktuellen und den vorherigen Szeneknoten zu Speichern und starten die Schleife.
Code:
   scene::ISceneNode* selectedSceneNode = 0;
   scene::ISceneNode* lastSelectedSceneNode = 0;

   
   int lastFPS = -1;

   while(device->run())
  if (device->isWindowActive())
   {
      driver->beginScene(true, true, 0);

      smgr->drawAll();
Danach zeichnen wir die gesamte Szene mit smgr->drawAll(). Wir brauchen das Dreieck der Welt auf welches wir schauen. Dazu brauchen wir den exakten Punkt im Quake 3 Level auf den wir schauen. Dafür erstellen wir eine 3d-Linie welche an der Position der Kamera startet und durch den lookAt-target dieser geht. Danach sagen wir dem Kollisionsmanager wenn diese Linie mit einem Dreieck dieser Welt kollidiert dann speichere es im „triangle selector“. Wenn ja, zeichnen wir das 3d-Dreieck und setzen die Position der Wand to the Schnittpunkt.
Code:
      core::line3d<f32> line;
      line.start = camera->getPosition();
      line.end = line.start +
         (camera->getTarget() - line.start).normalize() * 1000.0f;

      core::vector3df intersection;
      core::triangle3df tri;

      if (smgr->getSceneCollisionManager()->getCollisionPoint(
         line, selector, intersection, tri))
      {
         bill->setPosition(intersection);
            
         driver->setTransform(video::ETS_WORLD, core::matrix4());
         driver->setMaterial(material);
         driver->draw3DTriangle(tri, video::SColor(0,255,0,0));
      }
Eine andere Art die von der Irrlicht Engine unterstützt wird basiert auf Zeichen-Boxen (bounding boxes). Jeder Szeneknoten hat eine “bounding box”, und aufgrund dieser, bekommt man sehr schnell z.B. den Szeneknoten auf den die Kamera gerichtet ist. Erneut teilen wir dem Kollisionsmanager das nötige mit, und wenn wir einen Szeneknoten haben, deaktivieren wir das Licht damit an es auch sieht, falls es nicht die Wand oder das Quake 3 Level ist.
Code:
      selectedSceneNode = smgr->getSceneCollisionManager()->
          getSceneNodeFromCameraBB(camera);

      if (lastSelectedSceneNode)
         lastSelectedSceneNode->setMaterialFlag(
                video::EMF_LIGHTING, true);

      if (selectedSceneNode == q3node ||
           selectedSceneNode == bill)
         selectedSceneNode = 0;

      if (selectedSceneNode)
         selectedSceneNode->setMaterialFlag(
               video::EMF_LIGHTING, false);

      lastSelectedSceneNode = selectedSceneNode;
Das war's, wir haben das zeichnen beendet.
Code:
      driver->endScene();

      int fps = driver->getFPS();

      if (lastFPS != fps)
      {
        core::stringw str = L"Collision detection example - Irrlicht Engine [";
        str += driver->getName();
        str += "] FPS:";
        str += fps;

        device->setWindowCaption(str.c_str());
        lastFPS = fps;
      }
   }

   device->drop();
   
   return 0;
}


_________________
"Die folgende Aussage ist falsch. Die vorherige Aussage ist richtig."


Nach oben
 Profil  
 
Beiträge der letzten Zeit anzeigen:  Sortiere nach  
Ein neues Thema erstellen Auf das Thema antworten  [ 1 Beitrag ] 

Alle Zeiten sind UTC + 1 Stunde


Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 1 Gast


Du darfst keine neuen Themen in diesem Forum erstellen.
Du darfst keine Antworten zu Themen in diesem Forum erstellen.
Du darfst deine Beiträge in diesem Forum nicht ändern.
Du darfst deine Beiträge in diesem Forum nicht löschen.
Du darfst keine Dateianhänge in diesem Forum erstellen.

Suche nach:
Gehe zu:  
Powered by phpBB® Forum Software © phpBB Group
Deutsche Übersetzung durch phpBB.de