git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@6000 dfc29bdd-3216-0410-991c-e03cc46cb475
		
			
				
	
	
		
			309 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			309 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
| <html>
 | |
| <head>
 | |
| <title>Irrlicht Engine Tutorial</title>
 | |
| <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
 | |
| </head>
 | |
| 
 | |
| <body bgcolor="#FFFFFF" leftmargin="0" topmargin="0" marginwidth="0" marginheight="0">
 | |
| <br>
 | |
| <table width="95%" border="0" cellspacing="0" cellpadding="2" align="center">
 | |
|   <tr> 
 | |
|     <td bgcolor="#666699" width="10"><b><a href="http://irrlicht.sourceforge.net" target="_blank"><img src="../../media/irrlichtlogo.jpg" width="88" height="31" border="0"></a></b></td>
 | |
|     <td bgcolor="#666699" width="100%">
 | |
| <div align="center">
 | |
| <div align="center"></div>
 | |
|         <div align="left"><b><font color="#FFFFFF">Tutorial 7. Collision detection 
 | |
|           and response</font></b></div>
 | |
|       </div>
 | |
|       </td>
 | |
|   </tr>
 | |
|   <tr bgcolor="#eeeeff"> 
 | |
|     <td height="90" colspan="2"> 
 | |
|       <div align="left"> 
 | |
|         <p>In this tutorial, I will show how to collision detection with the Irrlicht 
 | |
|           Engine. I will describe 3 methods: Automatic collision detection for 
 | |
|           moving through 3d worlds with stair climbing and sliding, manual triangle 
 | |
|           picking and manual scene node picking.</p>
 | |
|         <p>The program which is described here will look like this:</p>
 | |
|         <p align="center"><img src="../../media/007shot.jpg" width="259" height="204"><br>
 | |
|         </p>
 | |
|       </div>
 | |
|     </td>
 | |
|   </tr>
 | |
| </table>
 | |
| <br>
 | |
| <table width="95%" border="0" cellspacing="0" cellpadding="2" align="center">
 | |
|   <tr> 
 | |
|     <td bgcolor="#666699"> <div align="center"><b><font color="#FFFFFF"></font></b></div>
 | |
|       <b><font color="#FFFFFF">Lets start!</font></b></td>
 | |
|   </tr>
 | |
|   <tr> 
 | |
|     <td height="90" bgcolor="#eeeeff" valign="top"> <div align="left"> 
 | |
|         <div align="left">
 | |
|           <p>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.</p>
 | |
|           <table width="95%" border="0" cellspacing="2" cellpadding="0" bgcolor="#CCCCCC" align="center">
 | |
|             <tr> 
 | |
|               <td> <pre><font size="2"><font color="#008000">#include <irrlicht.h>
 | |
| #include <iostream><br>
 | |
| </font><b>using namespace </b>irr;
 | |
| 
 | |
| <font color="#008000">#pragma comment(lib, "Irrlicht.lib")
 | |
| 
 | |
| </font><b>int </b>main()
 | |
| {
 | |
| <font color="#008000">  // let user select driver type</font>
 | |
| <br>  video::E_DRIVER_TYPE driverType;<br><br>  printf("Please select the driver you want for this example:\n"\<br>    " (a) Direct3D 9.0c\n (b) Direct3D 8.1\n (c) OpenGL 1.5\n"\<br>    " (d) Software Renderer\n (e) Apfelbaum Software Renderer\n"\<br>    " (f) NullDevice\n (otherKey) exit\n\n");<br><br>  char i;<br>  std::cin >> i;<br><br>  switch(i)<br>  {<br>  case 'a': driverType = video::EDT_DIRECT3D9;break;<br>  case 'b': driverType = video::EDT_DIRECT3D8;break;<br>  case 'c': driverType = video::EDT_OPENGL;   break;<br>  case 'd': driverType = video::EDT_SOFTWARE; break;<br>  case 'e': driverType = video::EDT_BURNINGSVIDEO;break;  <br>  case 'f': driverType = video::EDT_NULL;     break;<br>  default: return 0;<br>  }	<br>
 | |
| <font color="#008000">  // create device</font></font></pre> 
 | |
|                 <pre>  IrrlichtDevice *device = createDevice(driverType,
 | |
|      core::dimension2d<s32>(640, 480), 16, false);<br>
 | |
|   if (device == 0)<br>    return 1; // could not create selected driver.<br><br>  video::IVideoDriver* driver = device->getVideoDriver();<br>  scene::ISceneManager* smgr = device->getSceneManager();<br><br>  <font size="2">device->getFileSystem()->addZipFileArchive<br>      (<font color="#FF0000">"../../media/map-20kdm2.pk3"</font>);
 | |
| 
 | |
| 	
 | |
| 	scene::IAnimatedMesh* q3levelmesh = smgr->getMesh(<font color="#FF0000">"20kdm2.bsp"</font>);
 | |
| 	scene::ISceneNode* q3node = <font color="#800080">0</font>;
 | |
| 	
 | |
| 	<b>if </b>(q3levelmesh)
 | |
| 		q3node = smgr->addOctTreeSceneNode(q3levelmesh->getMesh(<font color="#800080">0</font>));
 | |
| </font></pre> 
 | |
|               </td>
 | |
|             </tr>
 | |
|           </table>
 | |
|           <p> 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.<br>
 | |
|             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.</p>
 | |
|           <table width="95%" border="0" cellspacing="2" cellpadding="0" bgcolor="#CCCCCC" align="center">
 | |
|             <tr> 
 | |
|               <td> <pre><font size="2">scene::ITriangleSelector* selector = <font color="#800080">0</font>;
 | |
| 	
 | |
| 	<b>if </b>(q3node)
 | |
| 	{		
 | |
| 		q3node->setPosition(core::vector3df(-<font color="#800080">1370</font>,-<font color="#800080">130</font>,-<font color="#800080">1400</font>));
 | |
| 
 | |
| 		selector = smgr->createOctTreeTriangleSelector(
 | |
|             q3levelmesh->getMesh(<font color="#800080">0</font>), q3node, <font color="#800080">128</font>);
 | |
| 		q3node->setTriangleSelector(selector);
 | |
| 	}</font></pre></td>
 | |
|             </tr>
 | |
|           </table>
 | |
|           <p> 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<br>
 | |
|             engine is really, really easy.<br>
 | |
|             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. <br>
 | |
|           </p>
 | |
|           <table width="95%" border="0" cellspacing="2" cellpadding="0" bgcolor="#CCCCCC" align="center">
 | |
|             <tr> 
 | |
|               <td> <pre><font size="2">	scene::ICameraSceneNode* camera = 	<br>		camera = smgr->addCameraSceneNodeFPS(<font color="#800080">0</font>,<font color="#800080">100.0f</font>,<font color="#800080">300.0f</font>);
 | |
| 	camera->setPosition(core::vector3df(-10<font color="#800080">0</font>,<font color="#800080">50</font>,-15<font color="#800080">0</font>));
 | |
| 
 | |
| 	scene::ISceneNodeAnimator* anim =<br>    smgr->createCollisionResponseAnimator(
 | |
| 		selector, camera, core::vector3df(<font color="#800080">30</font>,<font color="#800080">50</font>,<font color="#800080">30</font>),
 | |
| 		core::vector3df(<font color="#800080">0</font>,<font color="#800080">-3</font>,<font color="#800080">0</font>),
 | |
| 		core::vector3df(<font color="#800080">0</font>,<font color="#800080">50</font>,<font color="#800080">0</font>));<br>
 | |
| 		selector->drop();<br>
 | |
| 	camera->addAnimator(anim);
 | |
| 	anim->drop();</font></pre></td>
 | |
|             </tr>
 | |
|           </table>
 | |
|           <p> 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 <br>
 | |
|             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. :)</p>
 | |
|           <table width="95%" border="0" cellspacing="2" cellpadding="0" bgcolor="#CCCCCC" align="center">
 | |
|             <tr> 
 | |
|               <td> <pre><font size="2">	<font color="#0A246A"><i>// disable mouse cursor
 | |
| 
 | |
| </i></font>	device->getCursorControl()->setVisible(<b>false</b>);
 | |
| 
 | |
| 	<font color="#0A246A"><i>// add billboard
 | |
| 
 | |
| </i></font>	scene::IBillboardSceneNode * bill = smgr->addBillboardSceneNode();
 | |
| 	bill->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR );
 | |
| 	bill->setMaterialTexture(<font color="#800080">0</font>, driver->getTexture(<br>         <font color="#FF0000">"../../media/particle.bmp"</font>));
 | |
| 	bill->setMaterialFlag(video::EMF_LIGHTING, <b>false</b>);
 | |
| 	bill->setSize(core::dimension2d<f32>(<font color="#800080">20.0f</font>, <font color="#800080">20.0f</font>));
 | |
| 
 | |
| 	<font color="#0A246A"><i>// add 3 animated faeries.
 | |
| 
 | |
| </i></font>	video::SMaterial material;
 | |
| 	material.Texture1 = driver->getTexture(<font color="#FF0000"><br>         "../../media/faerie2.bmp"</font>);
 | |
| 	material.Lighting = <b>true</b>;
 | |
| 
 | |
| 	scene::IAnimatedMeshSceneNode* node = <font color="#800080">0</font>;
 | |
| 	scene::IAnimatedMesh* faerie = smgr->getMesh(<br>        <font color="#FF0000">"../../media/faerie.md2"</font>);
 | |
| 
 | |
| 	<b>if </b>(faerie)
 | |
| 	{
 | |
| 		node = smgr->addAnimatedMeshSceneNode(faerie);
 | |
| 		node->setPosition(core::vector3df(-<font color="#800080">70</font>,<font color="#800080">0</font>,-<font color="#800080">90</font>));
 | |
| 		node->setMD2Animation(scene::EMAT_RUN);
 | |
| 		node->getMaterial(<font color="#800080">0</font>) = material;
 | |
| 
 | |
| 		node = smgr->addAnimatedMeshSceneNode(faerie);
 | |
| 		node->setPosition(core::vector3df(-<font color="#800080">70</font>,<font color="#800080">0</font>,-<font color="#800080">30</font>));
 | |
| 		node->setMD2Animation(scene::EMAT_SALUTE);
 | |
| 		node->getMaterial(<font color="#800080">0</font>) = material;
 | |
| 
 | |
| 		node = smgr->addAnimatedMeshSceneNode(faerie);
 | |
| 		node->setPosition(core::vector3df(-<font color="#800080">70</font>,<font color="#800080">0</font>,-<font color="#800080">60</font>));
 | |
| 		node->setMD2Animation(scene::EMAT_JUMP);
 | |
| 		node->getMaterial(<font color="#800080">0</font>) = material;
 | |
| 	}
 | |
| 
 | |
| 	material.Texture1 = <font color="#800080">0</font>;
 | |
| 	material.Lighting = <b>false</b>;
 | |
| 
 | |
| 	<font color="#0A246A"><i>// Add a light
 | |
| 
 | |
| </i></font>	smgr->addLightSceneNode(<font color="#800080">0</font>, core::vector3df(-<font color="#800080">60</font>,<font color="#800080">100</font>,<font color="#800080">400</font>),
 | |
| 		video::SColorf(<font color="#800080">1.0f</font>,<font color="#800080">1.0f</font>,<font color="#800080">1.0f</font>,<font color="#800080">1.0f</font>),
 | |
| 		<font color="#800080">600.0f</font>);</font></pre></td>
 | |
|             </tr>
 | |
|           </table>
 | |
|           <p>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.</p>
 | |
|         </div>
 | |
|         <table width="95%" border="0" cellspacing="2" cellpadding="0" bgcolor="#CCCCCC" align="center">
 | |
|           <tr> 
 | |
|             <td> <pre><font size="2">	scene::ISceneNode* selectedSceneNode = <font color="#800080">0</font>;
 | |
| 	scene::ISceneNode* lastSelectedSceneNode = <font color="#800080">0</font>;
 | |
| 
 | |
| 	
 | |
| 	<b>int </b>lastFPS = -<font color="#800080">1</font>;
 | |
| 
 | |
| 	<b>while</b>(device->run())<br>  <strong>if</strong> (device->isWindowActive())
 | |
| 	{
 | |
| 		driver->beginScene(<b>true</b>, <b>true</b>, <font color="#800080">0</font>);
 | |
| 
 | |
| 		smgr->drawAll();</font></pre></td>
 | |
|           </tr>
 | |
|         </table>
 | |
|         <p> 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. </p>
 | |
|         <table width="95%" border="0" cellspacing="2" cellpadding="0" bgcolor="#CCCCCC" align="center">
 | |
|           <tr> 
 | |
|             <td><pre><font size="2">		core::line3d<f32> line;
 | |
| 		line.start = camera->getPosition();
 | |
| 		line.end = line.start +
 | |
|          (camera->getTarget() - line.start).normalize() * <font color="#800080">1000.0f</font>;
 | |
| 
 | |
| 		core::vector3df intersection;
 | |
| 		core::triangle3df tri;
 | |
| 
 | |
| 		<b>if </b>(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(<font color="#800080">0</font>,<font color="#800080">255</font>,<font color="#800080">0</font>,<font color="#800080">0</font>));
 | |
| 		}</font></pre></td>
 | |
|           </tr>
 | |
|         </table>
 | |
|         <p> 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<br>
 | |
|           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. </p>
 | |
|         <table width="95%" border="0" cellspacing="2" cellpadding="0" bgcolor="#CCCCCC" align="center">
 | |
|           <tr> 
 | |
|             <td><pre><font size="2">		selectedSceneNode = smgr->getSceneCollisionManager()->
 | |
|           getSceneNodeFromCameraBB(camera);
 | |
| 
 | |
| 		<b>if </b>(lastSelectedSceneNode)
 | |
| 			lastSelectedSceneNode->setMaterialFlag(
 | |
|                 video::EMF_LIGHTING, <b>true</b>);
 | |
| 
 | |
| 		<b>if </b>(selectedSceneNode == q3node ||
 | |
|            selectedSceneNode == bill)
 | |
| 			selectedSceneNode = <font color="#800080">0</font>;
 | |
| 
 | |
| 		<b>if </b>(selectedSceneNode)
 | |
| 			selectedSceneNode->setMaterialFlag(
 | |
|                video::EMF_LIGHTING, <b>false</b>);
 | |
| 
 | |
| 		lastSelectedSceneNode = selectedSceneNode;</font></pre></td>
 | |
|           </tr>
 | |
|         </table>
 | |
|         <p> That's it, we just have to finish drawing.</p>
 | |
|         <table width="95%" border="0" cellspacing="2" cellpadding="0" bgcolor="#CCCCCC" align="center">
 | |
|           <tr> 
 | |
|             <td> <pre><font size="2">		driver->endScene();
 | |
| 
 | |
| 		<b>int </b>fps = driver->getFPS();
 | |
| 
 | |
| 		<b>if </b>(lastFPS != fps)
 | |
| 		{
 | |
| 		  core::stringw str = L"Collision detection example - Irrlicht Engine [";<br>		  str += driver->getName();<br>		  str += "] FPS:";<br>		  str += fps;<br><br>		  device->setWindowCaption(str.c_str());<br>		  lastFPS = fps;<br>		}
 | |
| 	}
 | |
| 
 | |
| 	device->drop();
 | |
| 	
 | |
| 	<b>return </b><font color="#800080">0</font>;
 | |
| }
 | |
| 
 | |
| </font></pre></td>
 | |
|           </tr>
 | |
|         </table>
 | |
|         <p> </p>
 | |
|         <p> </p>
 | |
|         </div>
 | |
|       </td>
 | |
|   </tr>
 | |
| </table>
 | |
| <p> </p>
 | |
|       </body>
 | |
| </html>
 |