+
+
+ To start, we take the program from tutorial 2, which loaded and displayed
+ a quake 3 level. We will use the level to walk in it and to pick triangles
+ from it. In addition we'll place 3 animated models into it for scene
+ node picking. The following code starts up the engine and loads a
+ quake 3 level. I will not explain it, because it should already be
+ known from tutorial 2.
+
+
+ #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_BURNINGSVIDEO;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 far so good, we've loaded the quake 3 level like in tutorial
+ 2. Now, here comes something different: We create a triangle selector.
+ A triangle selector is a class which can fetch the triangles from
+ scene nodes for doing different things with them, for example collision
+ detection. There are different triangle selectors, and all can be
+ created with the ISceneManager. In this example, we create an OctTreeTriangleSelector,
+ which optimizes the triangle output a little bit by reducing it like
+ an octree. This is very useful for huge meshes like quake 3 levels.
+ Afte we created the triangle selector, we attach it to the q3node.
+ This is not necessary, but in this way, we do not need to care for
+ the selector, for example dropping it after we do not need it anymore.
+
+
+ scene::ITriangleSelector* selector = 0;
+
+ if (q3node)
+ {
+ q3node->setPosition(core::vector3df(-1370,-130,-1400));
+
+ selector = smgr->createOctTreeTriangleSelector(
+ q3levelmesh->getMesh(0), q3node, 128);
+ q3node->setTriangleSelector(selector);
+ } |
+
+
+ We add a first person shooter camera to the scene for being able
+ to move in the quake 3 level like in tutorial 2. But this, time, we
+ add a special animator to the camera: A Collision Response animator.
+ This thing modifies the scene node to which it is attached to in that
+ way, that it may no more move through walls and is affected by gravity.
+ The only thing we have to tell the animator is how the world looks
+ like, how big the scene node is, how gravity and so on. After the
+ collision response animator is attached to the camera, we do not have
+ to do anything more for collision detection, anything is done automaticly,
+ all other collision detection code below is for picking. And please
+ note another cool feature: The collsion response animator can be attached
+ also to all other scene nodes, not only to cameras. And it can be
+ mixed with other scene node animators. In this way, collision detection
+ and response in the Irrlicht
+ engine is really, really easy.
+ Now we'll take a closer look on the parameters of createCollisionResponseAnimator().
+ The first parameter is the TriangleSelector, which specifies how the
+ world, against collision detection is done looks like. The second
+ parameter is the scene node, which is the object, which is affected
+ by collision detection, in our case it is the camera. The third defines
+ how big the object is, it is the radius of an ellipsoid. Try it out
+ and change the radius to smaller values, the camera will be able to
+ move closer to walls after this. The next parameter is the direction
+ and speed of gravity. You could set it to (0,0,0) to disable gravity.
+ And the last value is just a translation: Without this, the ellipsoid
+ with which collision detection is done would be around the camera,
+ and the camera would be in the middle of the ellipsoid. But as human
+ beings, we are used to have our eyes on top of the body, with which
+ we collide with our world, not in the middle of it. So we place the
+ scene node 50 units over the center of the ellipsoid with this parameter.
+ And that's it, collision detection works now.
+
+
+
+ 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));
+ selector->drop();
+ camera->addAnimator(anim);
+ anim->drop(); |
+
+
+ Because collision detection is no big deal in irrlicht, I'll describe
+ how to do two different types of picking in the next section. But
+ before this, I'll prepare the scene a little. I need three animated
+ characters which we
+ could pick later, a dynamic light for lighting them, a billboard for
+ drawing where we found an intersection, and, yes, I need to get rid
+ of this mouse cursor. :)
+
+
+ // 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); |
+
+
+ For not making it to complicated, I'm doing picking inside the drawing
+ loop. We take two pointers for storing the current and the last selected
+ scene node and start the loop.
+
+
+
+ scene::ISceneNode* selectedSceneNode = 0;
+ scene::ISceneNode* lastSelectedSceneNode = 0;
+
+
+ int lastFPS = -1;
+
+ while(device->run()) if (device->isWindowActive())
+ {
+ driver->beginScene(true, true, 0);
+
+ smgr->drawAll(); |
+
+
+ After we've drawn the whole scene whit smgr->drawAll(), we'll do
+ the first picking: We want to know which triangle of the world we are
+ looking at. In addition, we want the exact point of the quake 3 level
+ we are looking at. For this, we create a 3d line starting at the position
+ of the camera and going through the lookAt-target of it. Then we ask
+ the collision manager if this line collides with a triangle of the world
+ stored in the triangle selector. If yes, we draw the 3d triangle and
+ set the position of the billboard to the intersection point.
+
+
+ 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));
+ } |
+
+
+ Another type of picking supported by the Irrlicht Engine is scene
+ node picking based on bouding boxes. Every scene node has got a bounding
+ box, and because of that, it's very fast for example to get the scene
+ node which the camera looks
+ at. Again, we ask the collision manager for this, and if we've got a
+ scene node, we highlight it by disabling Lighting in its material, if
+ it is not the billboard or the quake 3 level.
+
+
+ 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; |
+
+
+ That's it, we just have to finish drawing.
+
+
+ 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;
+}
+
+ |
+
+
+
+
+
+ |
+
+