123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385 |
- /** Example 007 Collision
- We will describe 2 methods: Automatic collision detection for moving through
- 3d worlds with stair climbing and sliding, and manual scene node and triangle
- picking using a ray. In this case, we will use a ray coming out from the
- camera, but you can use any ray.
- To start, we take the program from tutorial 2, which loads and displays a
- quake 3 level. We will use the level to walk in it and to pick triangles from.
- In addition we'll place 3 animated models into it for triangle picking. The
- following code starts up the engine and loads the level, as per tutorial 2.
- */
- #include <irrlicht.h>
- #include "driverChoice.h"
- #include "exampleHelper.h"
- using namespace irr;
- #ifdef _MSC_VER
- #pragma comment(lib, "Irrlicht.lib")
- #endif
- enum
- {
- // I use this ISceneNode ID to indicate a scene node that is
- // not pickable by getSceneNodeAndCollisionPointFromRay()
- ID_IsNotPickable = 0,
- // I use this flag in ISceneNode IDs to indicate that the
- // scene node can be picked by ray selection.
- IDFlag_IsPickable = 1 << 0,
- // I use this flag in ISceneNode IDs to indicate that the
- // scene node can be highlighted. In this example, the
- // homonids can be highlighted, but the level mesh can't.
- IDFlag_IsHighlightable = 1 << 1
- };
- int main()
- {
- // ask user for driver
- video::E_DRIVER_TYPE driverType=driverChoiceConsole();
- if (driverType==video::EDT_COUNT)
- return 1;
- // create device
- IrrlichtDevice *device =
- createDevice(driverType, core::dimension2d<u32>(640, 480), 16, false);
- if (device == 0)
- return 1; // could not create selected driver.
- /*
- If we want to receive information about the material of a hit triangle we have to get
- collisions per meshbuffer. The only disadvantage of this is that getting them per
- meshbuffer can be a little bit slower than per mesh, but usually that's not noticeable.
- If you set this to false you will no longer get material names in the title bar.
- */
- const bool separateMeshBuffers = true;
- video::IVideoDriver* driver = device->getVideoDriver();
- scene::ISceneManager* smgr = device->getSceneManager();
- const io::path mediaPath = getExampleMediaPath();
- device->getFileSystem()->addFileArchive(mediaPath + "map-20kdm2.pk3");
- scene::IAnimatedMesh* q3levelmesh = smgr->getMesh("20kdm2.bsp");
- scene::IMeshSceneNode* q3node = 0;
- // The Quake mesh is pickable, but doesn't get highlighted.
- if (q3levelmesh)
- q3node = smgr->addOctreeSceneNode(q3levelmesh->getMesh(0), 0, IDFlag_IsPickable);
- /*
- 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
- OctreeTriangleSelector, 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. After 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(-1350,-130,-1400));
- /*
- There is currently no way to split an octree by material.
- So if we need material infos we have to create one octree per
- meshbuffer and put them together in a MetaTriangleSelector.
- */
- if ( separateMeshBuffers && q3node->getMesh()->getMeshBufferCount() > 1)
- {
- scene::IMetaTriangleSelector * metaSelector = smgr->createMetaTriangleSelector();
- for ( irr::u32 m=0; m < q3node->getMesh()->getMeshBufferCount(); ++m )
- {
- scene::ITriangleSelector*
- bufferSelector = smgr->createOctreeTriangleSelector(
- q3node->getMesh()->getMeshBuffer(m), m, q3node);
- if ( bufferSelector )
- {
- metaSelector->addTriangleSelector( bufferSelector );
- bufferSelector->drop();
- }
- }
- selector = metaSelector;
- }
- else
- {
- // If you don't need material infos just create one octree for the
- // whole mesh.
- selector = smgr->createOctreeTriangleSelector(
- q3node->getMesh(), q3node, 128);
- }
- q3node->setTriangleSelector(selector);
- // We're not done with this selector yet, so don't drop it.
- }
- /*
- We add a first person shooter camera to the scene so that we can see and
- 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
- animator modifies the scene node to which it is attached in order to
- prevent it from moving through walls and to add gravity to the node. The
- only things we have to tell the animator is how the world looks like,
- how big the scene node is, how much gravity to apply and so on. After the
- collision response animator is attached to the camera, we do not have to do
- anything else for collision detection, it's all done automatically.
- The rest of the collision detection code below is for picking. And please
- note another cool feature: The collision 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 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 which 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. We'll set it to
- (0, -1000, 0), which approximates realistic gravity (depends on the units
- which are used in the scene model). You could set it to (0,0,0) to disable
- gravity. And the last value is just an offset: Without it 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, 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.
- */
- // Set a jump speed of 300 units per second, which gives a fairly realistic jump
- // when used with the gravity of (0, -1000, 0) in the collision response animator.
- scene::ICameraSceneNode* camera =
- smgr->addCameraSceneNodeFPS(0, 100.0f, .3f, ID_IsNotPickable, 0, 0, true, 300.f);
- camera->setPosition(core::vector3df(50,50,-60));
- camera->setTarget(core::vector3df(-70,30,-60));
- if (selector)
- {
- scene::ISceneNodeAnimatorCollisionResponse * anim = smgr->createCollisionResponseAnimator(
- selector, camera, core::vector3df(30,50,30),
- core::vector3df(0,-1000,0), core::vector3df(0,30,0));
- selector->drop(); // As soon as we're done with the selector, drop it.
- camera->addAnimator(anim);
- anim->drop(); // And likewise, drop the animator when we're done referring to it.
- }
- // Now I create three animated characters which we can pick, a dynamic light for
- // lighting them, and a billboard for drawing where we found an intersection.
- // First, let's get rid of the mouse cursor. We'll use a billboard to show
- // what we're looking at.
- device->getCursorControl()->setVisible(false);
- // Add the billboard.
- scene::IBillboardSceneNode * bill = smgr->addBillboardSceneNode();
- bill->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR );
- bill->setMaterialTexture(0, driver->getTexture(mediaPath + "particle.bmp"));
- bill->setMaterialFlag(video::EMF_LIGHTING, false);
- bill->setMaterialFlag(video::EMF_ZBUFFER, false);
- bill->setSize(core::dimension2d<f32>(20.0f, 20.0f));
- bill->setID(ID_IsNotPickable); // This ensures that we don't accidentally ray-pick it
- /* Add 3 animated hominids, which we can pick using a ray-triangle intersection.
- They all animate quite slowly, to make it easier to see that accurate triangle
- selection is being performed. */
- scene::IAnimatedMeshSceneNode* node = 0;
- // Add an MD2 node, which uses vertex-based animation.
- node = smgr->addAnimatedMeshSceneNode(smgr->getMesh(mediaPath + "faerie.md2"),
- 0, IDFlag_IsPickable | IDFlag_IsHighlightable);
- if ( node )
- {
- node->setPosition(core::vector3df(-90,-15,-140)); // Put its feet on the floor.
- node->setScale(core::vector3df(1.6f)); // Make it appear realistically scaled
- node->setMD2Animation(scene::EMAT_POINT);
- node->setAnimationSpeed(20.f);
- video::SMaterial& material = node->getMaterial(0);
- material.setTexture(0, driver->getTexture(mediaPath + "faerie2.bmp"));
- material.Lighting = true;
- material.NormalizeNormals = true;
- }
- // Now create a triangle selector for it. The selector will know that it
- // is associated with an animated node, and will update itself as necessary.
- selector = smgr->createTriangleSelector(node, separateMeshBuffers);
- node->setTriangleSelector(selector);
- selector->drop(); // We're done with this selector, so drop it now.
- // And this B3D file uses skinned skeletal animation.
- node = smgr->addAnimatedMeshSceneNode(smgr->getMesh(mediaPath + "ninja.b3d"),
- 0, IDFlag_IsPickable | IDFlag_IsHighlightable);
- if ( node )
- {
- node->setScale(core::vector3df(10));
- node->setPosition(core::vector3df(-75,-66,-80));
- node->setRotation(core::vector3df(0,90,0));
- node->setAnimationSpeed(8.f);
- node->getMaterial(0).NormalizeNormals = true;
- node->getMaterial(0).Lighting = true;
- // Just do the same as we did above.
- selector = smgr->createTriangleSelector(node, separateMeshBuffers);
- node->setTriangleSelector(selector);
- selector->drop();
- }
- // This X files uses skeletal animation, but without skinning.
- node = smgr->addAnimatedMeshSceneNode(smgr->getMesh(mediaPath + "dwarf.x"),
- 0, IDFlag_IsPickable | IDFlag_IsHighlightable);
- if ( node )
- {
- node->setPosition(core::vector3df(-70,-66,-30)); // Put its feet on the floor.
- node->setRotation(core::vector3df(0,-90,0)); // And turn it towards the camera.
- node->setAnimationSpeed(20.f);
- node->getMaterial(0).Lighting = true;
- selector = smgr->createTriangleSelector(node, separateMeshBuffers);
- node->setTriangleSelector(selector);
- selector->drop();
- }
- // And this mdl file uses skinned skeletal animation.
- node = smgr->addAnimatedMeshSceneNode(smgr->getMesh(mediaPath + "yodan.mdl"),
- 0, IDFlag_IsPickable | IDFlag_IsHighlightable);
- if ( node )
- {
- node->setPosition(core::vector3df(-90,-25,20));
- node->setScale(core::vector3df(0.8f));
- node->getMaterial(0).Lighting = true;
- node->setAnimationSpeed(20.f);
- // Just do the same as we did above.
- selector = smgr->createTriangleSelector(node, separateMeshBuffers);
- node->setTriangleSelector(selector);
- selector->drop();
- }
- // Add a light, so that the unselected nodes aren't completely dark.
- scene::ILightSceneNode * light = smgr->addLightSceneNode(0, core::vector3df(-60,100,400),
- video::SColorf(1.0f,1.0f,1.0f,1.0f), 600.0f);
- light->setID(ID_IsNotPickable); // Make it an invalid target for selection.
- // Remember which scene node is highlighted
- scene::ISceneNode* highlightedSceneNode = 0;
- scene::ISceneCollisionManager* collMan = smgr->getSceneCollisionManager();
- // draw the selection triangle only as wireframe
- irr::video::SMaterial materialWireframe;
- materialWireframe.Lighting = false;
- materialWireframe.Wireframe=true;
- while(device->run())
- if (device->isWindowActive())
- {
- driver->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, video::SColor(0));
- smgr->drawAll();
- // Unlight any currently highlighted scene node
- if (highlightedSceneNode)
- {
- highlightedSceneNode->setMaterialFlag(video::EMF_LIGHTING, true);
- highlightedSceneNode = 0;
- }
- // All intersections in this example are done with a ray cast out from the camera to
- // a distance of 1000. You can easily modify this to check (e.g.) a bullet
- // trajectory or a sword's position, or create a ray from a mouse click position using
- // ISceneCollisionManager::getRayFromScreenCoordinates()
- core::line3d<f32> ray;
- ray.start = camera->getPosition();
- ray.end = ray.start + (camera->getTarget() - ray.start).normalize() * 1000.0f;
- // This call is all you need to perform ray/triangle collision on every scene node
- // that has a triangle selector, including the Quake level mesh. It finds the nearest
- // collision point/triangle, and returns the scene node containing that point.
- // Irrlicht provides other types of selection, including ray/triangle selector,
- // ray/box and ellipse/triangle selector, plus associated helpers.
- // You might also want to check the other methods of ISceneCollisionManager.
- irr::io::SNamedPath hitTextureName;
- scene::SCollisionHit hitResult;
- scene::ISceneNode * selectedSceneNode =collMan->getSceneNodeAndCollisionPointFromRay(
- hitResult, // Returns all kind of info about the collision
- ray,
- IDFlag_IsPickable, // This ensures that only nodes that we have
- // set up to be pickable are considered
- 0); // Check the entire scene (this is actually the implicit default)
- // If the ray hit anything, move the billboard to the collision position
- // and draw the triangle that was hit.
- if(selectedSceneNode)
- {
- bill->setPosition(hitResult.Intersection); // Show the current intersection point with the level or a mesh
- // We need to reset the transform before doing our own rendering.
- driver->setTransform(video::ETS_WORLD, core::matrix4());
- driver->setMaterial(materialWireframe);
- driver->draw3DTriangle(hitResult.Triangle, video::SColor(0,255,0,0)); // Show which triangle has been hit
- // We can check the flags for the scene node that was hit to see if it should be
- // highlighted. The animated nodes can be highlighted, but not the Quake level mesh
- if((selectedSceneNode->getID() & IDFlag_IsHighlightable) == IDFlag_IsHighlightable)
- {
- highlightedSceneNode = selectedSceneNode;
- // Highlighting in this case means turning lighting OFF for this node,
- // which means that it will be drawn with full brightness.
- highlightedSceneNode->setMaterialFlag(video::EMF_LIGHTING, false);
- }
- // When separateMeshBuffers is set to true we can now find out which material was hit
- if ( hitResult.MeshBuffer && hitResult.Node && hitResult.Node->getMaterial(hitResult.MaterialIndex).TextureLayer[0].Texture )
- {
- // Note we are interested in the node material and not in the meshbuffer material.
- // Otherwise we wouldn't get the fairy2 texture which is only set on the node.
- hitTextureName = hitResult.Node->getMaterial(hitResult.MaterialIndex).TextureLayer[0].Texture->getName();
- }
- }
- // We're all done drawing, so end the scene.
- driver->endScene();
- // Show some info in title-bar
- int fps = driver->getFPS();
- static core::stringw lastString;
- core::stringw str = L"Collision detection example - Irrlicht Engine [";
- str += driver->getName();
- str += "] FPS:";
- str += fps;
- if ( !hitTextureName.getInternalName().empty() )
- {
- str += " ";
- irr::io::path texName(hitTextureName.getInternalName());
- str += core::deletePathFromFilename(texName);
- }
- if ( str != lastString ) // changing caption is somewhat expensive, so don't when nothing changed
- {
- device->setWindowCaption(str.c_str());
- lastString = str;
- }
- }
- device->drop();
- return 0;
- }
- /*
- **/
|