- Triangle selectors created from animated mesh scene nodes will update themselves as required to stay in sync with the node.
 - ISceneCollisionManager::getSceneNodeAndCollisionPointFromRay() allows selection by BB and triangle on a heirarchy of scene nodes.
Example 07 updated to show the usage of ISceneCollisionManager::getSceneNodeAndCollisionPointFromRay(), used on animated meshes.

git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@2177 dfc29bdd-3216-0410-991c-e03cc46cb475
master
Rogerborg 2009-02-01 18:56:05 +00:00
parent 78f9dee79c
commit 5478f80545
10 changed files with 485 additions and 163 deletions

View File

@ -463,6 +463,10 @@ Changes in version 1.6, TA
Changes in version 1.6 Changes in version 1.6
- ISceneCollisionManager::getSceneNodeAndCollisionPointFromRay() allows selection by BB and triangle on a heirarchy of scene nodes.
- Triangle selectors created from animated mesh scene nodes will update themselves as required to stay in sync with the node.
- IVideoDriver has methods to enumerate the available image loaders and writers. - IVideoDriver has methods to enumerate the available image loaders and writers.
- Octtree scene nodes are now IMeshSceneNodes rather than ISceneNodes, and so you can call getMesh() on them. - Octtree scene nodes are now IMeshSceneNodes rather than ISceneNodes, and so you can call getMesh() on them.

View File

@ -1,15 +1,14 @@
/** Example 007 Collision /** Example 007 Collision
In this tutorial, I will show how to detect collisions with the Irrlicht Engine. We will describe 2 methods: Automatic collision detection for moving through 3d worlds
I will describe 3 methods: Automatic collision detection for moving through 3d with stair climbing and sliding, and manual scene node and triangle picking using a
worlds with stair climbing and sliding, manual triangle picking, and manual ray. In this case, we will use a ray coming out from the camera, but you can use
scene node picking. any ray.
To start, we take the program from tutorial 2, which loads and displays a quake 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 it. In 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 scene node picking. The addition we'll place 3 animated models into it for triangle picking. The
following code starts up the engine and loads a quake 3 level. I will not following code starts up the engine and loads a quake 3 level, as per tutorial 2.
explain it, because it should already be known from tutorial 2.
*/ */
#include <irrlicht.h> #include <irrlicht.h>
#include <iostream> #include <iostream>
@ -20,6 +19,22 @@ using namespace irr;
#pragma comment(lib, "Irrlicht.lib") #pragma comment(lib, "Irrlicht.lib")
#endif #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() int main()
{ {
// let user select driver type // let user select driver type
@ -61,10 +76,9 @@ int main()
scene::IAnimatedMesh* q3levelmesh = smgr->getMesh("20kdm2.bsp"); scene::IAnimatedMesh* q3levelmesh = smgr->getMesh("20kdm2.bsp");
scene::IMeshSceneNode* q3node = 0; scene::IMeshSceneNode* q3node = 0;
// The Quake mesh is pickable, but doesn't get highlighted.
if (q3levelmesh) if (q3levelmesh)
q3node = smgr->addOctTreeSceneNode(q3levelmesh->getMesh(0)); q3node = smgr->addOctTreeSceneNode(q3levelmesh->getMesh(0), 0, IDFlag_IsPickable);
q3node->setID(0); // Make it an invalid target for bounding box collision
/* /*
So far so good, we've loaded the quake 3 level like in tutorial 2. Now, So far so good, we've loaded the quake 3 level like in tutorial 2. Now,
@ -90,11 +104,12 @@ int main()
selector = smgr->createOctTreeTriangleSelector( selector = smgr->createOctTreeTriangleSelector(
q3node->getMesh(), q3node, 128); q3node->getMesh(), q3node, 128);
q3node->setTriangleSelector(selector); 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 for being able to 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 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 special animator to the camera: A Collision Response animator. This
animator modifies the scene node to which it is attached to in order to animator modifies the scene node to which it is attached to in order to
@ -102,12 +117,12 @@ int main()
only thing we have to tell the animator is how the world looks like, only thing 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 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 collision response animator is attached to the camera, we do not have to do
anything more for collision detection, anything is done automatically, anything more for collision detection, anything is done automatically.
all other collision detection code below is for picking. And please The rest of the collision detection code below is for picking. And please
note another cool feature: The collision response animator can be note another cool feature: The collision response animator can be
attached also to all other scene nodes, not only to cameras. And it can 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 be mixed with other scene node animators. In this way, collision
detection and response in the Irrlicht engine is really, really easy. detection and response in the Irrlicht engine is really easy.
Now we'll take a closer look on the parameters of Now we'll take a closer look on the parameters of
createCollisionResponseAnimator(). The first parameter is the createCollisionResponseAnimator(). The first parameter is the
@ -132,9 +147,8 @@ int main()
// Set a jump speed of 3 units per second, which gives a fairly realistic jump // Set a jump speed of 3 units per second, which gives a fairly realistic jump
// when used with the gravity of (0, -10, 0) in the collision response animator. // when used with the gravity of (0, -10, 0) in the collision response animator.
scene::ICameraSceneNode* camera = scene::ICameraSceneNode* camera =
smgr->addCameraSceneNodeFPS(0, 100.0f, .3f, -1, 0, 0, true, 3.f); smgr->addCameraSceneNodeFPS(0, 100.0f, .3f, ID_IsNotPickable, 0, 0, true, 3.f);
camera->setPosition(core::vector3df(-100,50,-150)); camera->setPosition(core::vector3df(-100,50,-150));
camera->setID(0); // Make it an invalid target for bounding box collision
if (selector) if (selector)
{ {
@ -142,165 +156,154 @@ int main()
selector, camera, core::vector3df(30,50,30), selector, camera, core::vector3df(30,50,30),
core::vector3df(0,-10,0), core::vector3df(0,-10,0),
core::vector3df(0,50,0)); core::vector3df(0,50,0));
selector->drop(); // As soon as we're done with the selector, drop it.
camera->addAnimator(anim); camera->addAnimator(anim);
anim->drop(); 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
Because collision detection is no big deal in irrlicht, I'll describe how to // lighting them, and a billboard for drawing where we found an intersection.
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
// 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); device->getCursorControl()->setVisible(false);
// add billboard // Add the billboard.
scene::IBillboardSceneNode * bill = smgr->addBillboardSceneNode(); scene::IBillboardSceneNode * bill = smgr->addBillboardSceneNode();
bill->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR ); bill->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR );
bill->setMaterialTexture(0, driver->getTexture("../../media/particle.bmp")); bill->setMaterialTexture(0, driver->getTexture("../../media/particle.bmp"));
bill->setMaterialFlag(video::EMF_LIGHTING, false); bill->setMaterialFlag(video::EMF_LIGHTING, false);
bill->setMaterialFlag(video::EMF_ZBUFFER, false); bill->setMaterialFlag(video::EMF_ZBUFFER, false);
bill->setSize(core::dimension2d<f32>(20.0f, 20.0f)); bill->setSize(core::dimension2d<f32>(20.0f, 20.0f));
bill->setID(0); // Make it an invalid target for bounding box collision bill->setID(ID_IsNotPickable); // This ensures that we don't accidentally ray-pick it
// add 3 animated faeries. We'll make their bounding boxes visible so // Add 3 animated hominids, which we can pick using a ray-triangle intersection.
// that we can see the same boxes that the scene collision manager is // They all animate quite slowly, to make it easier to see that accurate triangle
// using to perform the getSceneNodeFromCameraBB() check below. // selection is being performed.
scene::IAnimatedMeshSceneNode* node = 0;
// Add an MD2 node, which uses vertex-based animation.
node = smgr->addAnimatedMeshSceneNode(smgr->getMesh("../../media/faerie.md2"),
0,
IDFlag_IsPickable | IDFlag_IsHighlightable);
node->setPosition(core::vector3df(-70,-25,-120)); // Put its feet on the floor.
node->setScale(core::vector3df(2, 2, 2)); // Make it appear realistically scaled
node->setMD2Animation(scene::EMAT_RUN);
node->setAnimationSpeed(20.f);
video::SMaterial material; video::SMaterial material;
material.setTexture(0, driver->getTexture("../../media/faerie2.bmp")); material.setTexture(0, driver->getTexture("../../media/faerie2.bmp"));
material.Lighting = true; 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->getMaterial(0) = material;
node->setDebugDataVisible(scene::EDS_BBOX_ALL);
node = smgr->addAnimatedMeshSceneNode(faerie); // Now create a triangle selector for it. The selector will know that it
node->setPosition(core::vector3df(-70,0,-30)); // is associated with an animated node, and will update itself as necessary.
node->setMD2Animation(scene::EMAT_SALUTE); selector = smgr->createTriangleSelector(node);
node->getMaterial(0) = material; node->setTriangleSelector(selector);
node->setDebugDataVisible(scene::EDS_BBOX_ALL); selector->drop(); // We're done with this selector, so drop it now.
node = smgr->addAnimatedMeshSceneNode(faerie); // This X files uses skeletal animation, but without skinning.
node->setPosition(core::vector3df(-70,0,-60)); node = smgr->addAnimatedMeshSceneNode(smgr->getMesh("../../media/dwarf.x"),
node->setMD2Animation(scene::EMAT_JUMP); 0,
node->getMaterial(0) = material; IDFlag_IsPickable | IDFlag_IsHighlightable);
node->setDebugDataVisible(scene::EDS_BBOX_ALL); node->setPosition(core::vector3df(-70,-66,0)); // Put its feet on the floor.
} node->setRotation(core::vector3df(0,-90,0)); // And turn it towards the camera.
node->setAnimationSpeed(20.f);
selector = smgr->createTriangleSelector(node);
node->setTriangleSelector(selector);
selector->drop();
// And this B3D file uses skinned skeletal animation.
node = smgr->addAnimatedMeshSceneNode(smgr->getMesh("../../media/ninja.b3d"),
0,
IDFlag_IsPickable | IDFlag_IsHighlightable);
node->setScale(core::vector3df(10, 10, 10));
node->setPosition(core::vector3df(-70,-66,-60));
node->setRotation(core::vector3df(0,90,0));
node->setAnimationSpeed(10.f);
// Just do the same as we did above.
selector = smgr->createTriangleSelector(node);
node->setTriangleSelector(selector);
selector->drop();
material.setTexture(0, 0); material.setTexture(0, 0);
material.Lighting = false; material.Lighting = false;
// Add a light // Add a light, so that the unselected nodes aren't completely dark.
scene::ILightSceneNode * light = smgr->addLightSceneNode(0, core::vector3df(-60,100,400), scene::ILightSceneNode * light = smgr->addLightSceneNode(0, core::vector3df(-60,100,400),
video::SColorf(1.0f,1.0f,1.0f,1.0f), video::SColorf(1.0f,1.0f,1.0f,1.0f),
600.0f); 600.0f);
light->setID(0); // Make it an invalid target for bounding box collision light->setID(ID_IsNotPickable); // Make it an invalid target for selection.
/*
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;
// Remember which scene node is highlighted
scene::ISceneNode* highlightedSceneNode = 0;
scene::ISceneCollisionManager* collMan = smgr->getSceneCollisionManager();
int lastFPS = -1; int lastFPS = -1;
while(device->run()) while(device->run())
if (device->isWindowActive()) if (device->isWindowActive())
{ {
driver->beginScene(true, true, 0); driver->beginScene(true, true, 0);
smgr->drawAll(); smgr->drawAll();
/* // Unlight any currently highlighted scene node
After we've drawn the whole scene with smgr->drawAll(), we'll if (highlightedSceneNode)
do the first picking: We want to know which triangle of the {
world we are looking at. In addition, we want the exact point highlightedSceneNode->setMaterialFlag(video::EMF_LIGHTING, true);
of the quake 3 level we are looking at. For this, we create a highlightedSceneNode = 0;
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; // All intersections in this example are done with a ray cast out from the camera to
line.start = camera->getPosition(); // a distance of 1000. You can easily modify this to check (e.g.) a bullet
line.end = line.start + (camera->getTarget() - line.start).normalize() * 1000.0f; // 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;
// Tracks the current intersection point with the level or a mesh
core::vector3df intersection; core::vector3df intersection;
core::triangle3df tri; // Used to show with triangle has been hit
const scene::ISceneNode* hitNode; core::triangle3df hitTriangle;
if (smgr->getSceneCollisionManager()->getCollisionPoint( // This call is all you need to perform ray/triangle collision on every scene node
line, selector, intersection, tri, hitNode)) // 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.
// See the methods of ISceneCollisionManager
scene::ISceneNode * selectedSceneNode = collMan->getSceneNodeAndCollisionPointFromRay(
ray,
intersection, // This will be the position of the collision
hitTriangle, // This will be the triangle hit in the collision
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(intersection); bill->setPosition(intersection);
// We need to reset the transform before doing our own rendering.
driver->setTransform(video::ETS_WORLD, core::matrix4()); driver->setTransform(video::ETS_WORLD, core::matrix4());
driver->setMaterial(material); driver->setMaterial(material);
driver->draw3DTriangle(tri, video::SColor(0,255,0,0)); driver->draw3DTriangle(hitTriangle, video::SColor(0,255,0,0));
// 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);
}
} }
// We're all done drawing, so end the scene.
/*
Another type of picking supported by the Irrlicht Engine is
scene node picking based on bounding 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.
We use a collision bitmask of 1, i.e. any scene node with a scene ID
with bit 1 set is valid for collision. Scene nodes ID defaults to 0xFFFFFFFF
so all nodes will have this bit by default. We have called setID(0) on
the nodes that we don't care about in order to clear this bit and make
them invalid targets for collision. This makes the test a bit more
efficient, and also stops us from accidentally picking unexpected nodes,
e.g. the quake3 level, camera, light and billboard nodes. By default,
these *are* valid targets for bounding box selection.
*/
selectedSceneNode =
smgr->getSceneCollisionManager()->getSceneNodeFromCameraBB(camera, 1);
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(); driver->endScene();
int fps = driver->getFPS(); int fps = driver->getFPS();
@ -317,7 +320,6 @@ int main()
} }
} }
selector->drop();
device->drop(); device->drop();
return 0; return 0;

View File

@ -121,29 +121,29 @@ namespace scene
virtual ISceneNode* getSceneNodeFromScreenCoordinatesBB(const core::position2d<s32> & pos, virtual ISceneNode* getSceneNodeFromScreenCoordinatesBB(const core::position2d<s32> & pos,
s32 idBitMask=0, bool bNoDebugObjects = false) = 0; s32 idBitMask=0, bool bNoDebugObjects = false) = 0;
//! Get the nearest scene node which collides with a 3d ray and whose id matches a bitmask. //! Returns the nearest scene node which collides with a 3d ray and whose id matches a bitmask.
/** The collision tests are done using a bounding box for each /** The collision tests are done using a bounding box for each scene node.
scene node.
\param ray: Line with witch collisions are tested. \param ray: Line with witch collisions are tested.
\param idBitMask: Only scene nodes with an id with bits set \param idBitMask: Only scene nodes with an id which matches at least one of the
like in this mask will be tested. If the BitMask is 0, this bits contained in this mask will be tested. However, if this parameter is 0, then
feature is disabled. all nodes are checked.
\param bNoDebugObjects: Doesn't take debug objects into account \param bNoDebugObjects: Doesn't take debug objects into account when true. These
when true. These are scene nodes with IsDebugObject() = true. are scene nodes with IsDebugObject() = true.
\return Scene node nearest to ray.start, which collides with \return Returns the scene node nearest to ray.start, which collides with the
the ray and matches the idBitMask, if the mask is not null. If ray and matches the idBitMask, if the mask is not null. If no scene
no scene node is found, 0 is returned. */ node is found, 0 is returned. */
virtual ISceneNode* getSceneNodeFromRayBB(const core::line3d<f32> & ray, virtual ISceneNode* getSceneNodeFromRayBB(const core::line3d<f32> ray,
s32 idBitMask=0, bool bNoDebugObjects = false) = 0; s32 idBitMask=0, bool bNoDebugObjects = false) = 0;
//! Get the scene node, which the overgiven camera is looking at and whose id matches the bitmask. //! Get the scene node, which the given camera is looking at and whose id matches the bitmask.
/** A ray is simply casted from the position of the camera to /** A ray is simply casted from the position of the camera to
the view target position, and all scene nodes are tested the view target position, and all scene nodes are tested
against this ray. The collision tests are done using a bounding against this ray. The collision tests are done using a bounding
box for each scene node. box for each scene node.
\param camera: Camera from which the ray is casted. \param camera: Camera from which the ray is casted.
\param idBitMask: Only scene nodes with an id with bits set \param idBitMask: Only scene nodes with an id which matches at least one of the
like in this mask will be tested. If the BitMask is 0, this bits contained in this mask will be tested. However, if this parameter is 0, then
all nodes are checked.
feature is disabled. feature is disabled.
\param bNoDebugObjects: Doesn't take debug objects into account \param bNoDebugObjects: Doesn't take debug objects into account
when true. These are scene nodes with IsDebugObject() = true. when true. These are scene nodes with IsDebugObject() = true.
@ -152,6 +152,41 @@ namespace scene
no scene node is found, 0 is returned. */ no scene node is found, 0 is returned. */
virtual ISceneNode* getSceneNodeFromCameraBB(ICameraSceneNode* camera, virtual ISceneNode* getSceneNodeFromCameraBB(ICameraSceneNode* camera,
s32 idBitMask=0, bool bNoDebugObjects = false) = 0; s32 idBitMask=0, bool bNoDebugObjects = false) = 0;
//! Perform a ray/box and ray/triangle collision check on a heirarchy of scene nodes.
/** This checks all scene nodes under the specified one, first by ray/bounding
box, and then by accurate ray/triangle collision, finding the nearest collision,
and the scene node containg it. It returns the node hit, and (via output
parameters) the position of the collision, and the triangle that was hit.
All scene nodes in the hierarchy tree under the specified node are checked. Only
notes that are visible, with an ID that matches at least one bit in the supplied
bitmask, and which have a triangle selector are considered as candidates for being hit.
You do not have to build a meta triangle selector; the individual triangle selectors
of each candidate scene node are used automatically.
\param ray: Line with which collisions are tested.
\param outCollisionPoint: If a collision is detected, this will contain the
position of the nearest collision.
\param outTriangle: If a collision is detected, this will contain the triangle
with which the ray collided.
\param idBitMask: Only scene nodes with an id which matches at least one of the
bits contained in this mask will be tested. However, if this parameter is 0, then
all nodes are checked.
\param collisionRootNode: the scene node at which to begin checking. Only this
node and its children will be checked. If you want to check the entire scene,
pass 0, and the root scene node will be used (this is the default).
\param noDebugObjects: when true, debug objects are not considered viable targets.
Debug objects are scene nodes with IsDebugObject() = true.
\return Returns the scene node containing the hit triangle nearest to ray.start.
If no collision is detected, then 0 is returned. */
virtual ISceneNode* getSceneNodeAndCollisionPointFromRay(
core::line3df ray,
core::vector3df & outCollisionPoint,
core::triangle3df & outTriangle,
s32 idBitMask = 0,
ISceneNode * collisionRootNode = 0,
bool noDebugObjects = false) = 0;
}; };

View File

@ -1170,6 +1170,14 @@ namespace scene
See IReferenceCounted::drop() for more information. */ See IReferenceCounted::drop() for more information. */
virtual ITriangleSelector* createTriangleSelector(IMesh* mesh, ISceneNode* node) = 0; virtual ITriangleSelector* createTriangleSelector(IMesh* mesh, ISceneNode* node) = 0;
//! Creates a simple ITriangleSelector, based on an animated mesh scene node.
//! Details of the mesh associated with the node will be extracted internally.
//! Call ITriangleSelector::update() to have the triangle selector updated based
//! on the current frame of the animated mesh scene node.
//! \param: The animated mesh scene node from which to build the selector
virtual ITriangleSelector* createTriangleSelector(IAnimatedMeshSceneNode* node) = 0;
//! Creates a simple dynamic ITriangleSelector, based on a axis aligned bounding box. //! Creates a simple dynamic ITriangleSelector, based on a axis aligned bounding box.
/** Triangle selectors /** Triangle selectors
can be used for doing collision detection. Every time when triangles are can be used for doing collision detection. Every time when triangles are

View File

@ -57,7 +57,7 @@ ISceneNode* CSceneCollisionManager::getSceneNodeFromScreenCoordinatesBB(
//! Returns the nearest scene node which collides with a 3d ray and //! Returns the nearest scene node which collides with a 3d ray and
//! which id matches a bitmask. //! which id matches a bitmask.
ISceneNode* CSceneCollisionManager::getSceneNodeFromRayBB(const core::line3d<f32> & ray, ISceneNode* CSceneCollisionManager::getSceneNodeFromRayBB(const core::line3d<f32> ray,
s32 idBitMask, s32 idBitMask,
bool bNoDebugObjects) bool bNoDebugObjects)
{ {
@ -214,6 +214,122 @@ void CSceneCollisionManager::getPickedNodeBB(ISceneNode* root,
} }
ISceneNode* CSceneCollisionManager::getSceneNodeAndCollisionPointFromRay(
core::line3df ray,
core::vector3df & outCollisionPoint,
core::triangle3df & outTriangle,
s32 idBitMask,
ISceneNode * collisionRootNode,
bool noDebugObjects)
{
ISceneNode* bestNode = 0;
f32 bestDistanceSquared = FLT_MAX;
if(0 == collisionRootNode)
collisionRootNode = SceneManager->getRootSceneNode();
// We don't try to do anything too clever, like sorting the candidate
// nodes by distance to bounding-box. In the example below, we could do the
// triangle collision check with node A first, but we'd have to check node B
// anyway, as the actual collision point could be (and is) closer than the
// collision point in node A.
//
// ray end
// |
// AAAAAAAAAA
// A |
// A | B
// A | B
// A BBBBB
// A |
// A |
// |
// |
// ray start
//
// We therefore have to do a full BB and triangle collision on every scene
// node in order to find the nearest collision point, so sorting them by
// bounding box would be pointless.
getPickedNodeFromBBAndSelector(collisionRootNode,
ray,
idBitMask,
noDebugObjects,
bestDistanceSquared,
bestNode,
outCollisionPoint,
outTriangle);
return bestNode;
}
void CSceneCollisionManager::getPickedNodeFromBBAndSelector(
ISceneNode * root,
const core::line3df & ray,
s32 bits,
bool noDebugObjects,
f32 & outBestDistanceSquared,
ISceneNode * & outBestNode,
core::vector3df & outBestCollisionPoint,
core::triangle3df & outBestTriangle)
{
const core::list<ISceneNode*>& children = root->getChildren();
core::list<ISceneNode*>::ConstIterator it = children.begin();
for (; it != children.end(); ++it)
{
ISceneNode* current = *it;
ITriangleSelector * selector = current->getTriangleSelector();
if (selector && current->isVisible() &&
(noDebugObjects ? !current->isDebugObject() : true) &&
(bits==0 || (bits != 0 && (current->getID() & bits))))
{
// get world to object space transform
core::matrix4 mat;
if (!current->getAbsoluteTransformation().getInverse(mat))
continue;
// transform vector from world space to object space
core::line3df line(ray);
mat.transformVect(line.start);
mat.transformVect(line.end);
const core::aabbox3df& box = current->getBoundingBox();
core::vector3df candidateCollisionPoint;
core::triangle3df candidateTriangle;
// do intersection test in object space
const ISceneNode * hitNode = 0;
if (box.intersectsWithLine(line)
&&
getCollisionPoint(ray, selector, candidateCollisionPoint, candidateTriangle, hitNode))
{
const f32 distanceSquared = (candidateCollisionPoint - ray.start).getLengthSQ();
if(distanceSquared < outBestDistanceSquared)
{
outBestDistanceSquared = distanceSquared;
outBestNode = current;
outBestCollisionPoint = candidateCollisionPoint;
outBestTriangle = candidateTriangle;
}
}
}
getPickedNodeFromBBAndSelector(current,
ray,
bits,
noDebugObjects,
outBestDistanceSquared,
outBestNode,
outBestCollisionPoint,
outBestTriangle);
}
}
//! Returns the scene node, at which the overgiven camera is looking at and //! Returns the scene node, at which the overgiven camera is looking at and
//! which id matches the bitmask. //! which id matches the bitmask.

View File

@ -32,9 +32,8 @@ namespace scene
//! Returns the nearest scene node which collides with a 3d ray and //! Returns the nearest scene node which collides with a 3d ray and
//! which id matches a bitmask. //! which id matches a bitmask.
virtual ISceneNode* getSceneNodeFromRayBB(const core::line3d<f32> & ray, virtual ISceneNode* getSceneNodeFromRayBB(const core::line3d<f32> ray,
s32 idBitMask=0, s32 idBitMask=0, bool bNoDebugObjects = false);
bool bNoDebugObjects = false);
//! Returns the scene node, at which the overgiven camera is looking at and //! Returns the scene node, at which the overgiven camera is looking at and
//! which id matches the bitmask. //! which id matches the bitmask.
@ -69,6 +68,17 @@ namespace scene
virtual core::position2d<s32> getScreenCoordinatesFrom3DPosition( virtual core::position2d<s32> getScreenCoordinatesFrom3DPosition(
const core::vector3df & pos, ICameraSceneNode* camera=0); const core::vector3df & pos, ICameraSceneNode* camera=0);
//! Gets the scene node and nearest collision point for a ray based on
//! the nodes' id bitmasks, bounding boxes and triangle selectors.
virtual ISceneNode* getSceneNodeAndCollisionPointFromRay(
core::line3df ray,
core::vector3df & outCollisionPoint,
core::triangle3df & outTriangle,
s32 idBitMask = 0,
ISceneNode * collisionRootNode = 0,
bool noDebugObjects = false);
private: private:
//! recursive method for going through all scene nodes //! recursive method for going through all scene nodes
@ -79,6 +89,17 @@ namespace scene
f32& outbestdistance, f32& outbestdistance,
ISceneNode*& outbestnode); ISceneNode*& outbestnode);
//! recursive method for going through all scene nodes
void getPickedNodeFromBBAndSelector(ISceneNode * root,
const core::line3df & ray,
s32 bits,
bool noDebugObjects,
f32 & outBestDistanceSquared,
ISceneNode * & outBestNode,
core::vector3df & outBestCollisionPoint,
core::triangle3df & outBestTriangle);
struct SCollisionData struct SCollisionData
{ {
core::vector3df eRadius; core::vector3df eRadius;

View File

@ -1662,6 +1662,16 @@ ITriangleSelector* CSceneManager::createTriangleSelector(IMesh* mesh, ISceneNode
} }
//! Creates a simple and updatable ITriangleSelector, based on a the mesh owned by an
//! animated scene node
ITriangleSelector* CSceneManager::createTriangleSelector(IAnimatedMeshSceneNode* node)
{
if(!node || !node->getMesh())
return 0;
return new CTriangleSelector(node);
}
//! Creates a simple dynamic ITriangleSelector, based on a axis aligned bounding box. //! Creates a simple dynamic ITriangleSelector, based on a axis aligned bounding box.
ITriangleSelector* CSceneManager::createTriangleSelectorFromBoundingBox(ISceneNode* node) ITriangleSelector* CSceneManager::createTriangleSelectorFromBoundingBox(ISceneNode* node)
{ {

View File

@ -327,6 +327,13 @@ namespace scene
//! Creates a simple ITriangleSelector, based on a mesh. //! Creates a simple ITriangleSelector, based on a mesh.
virtual ITriangleSelector* createTriangleSelector(IMesh* mesh, ISceneNode* node); virtual ITriangleSelector* createTriangleSelector(IMesh* mesh, ISceneNode* node);
//! Creates a simple ITriangleSelector, based on an animated mesh scene node.
//! Details of the mesh associated with the node will be extracted internally.
//! Call ITriangleSelector::update() to have the triangle selector updated based
//! on the current frame of the animated mesh scene node.
//! \param: The animated mesh scene node from which to build the selector
virtual ITriangleSelector* createTriangleSelector(IAnimatedMeshSceneNode* node);
//! Creates a simple ITriangleSelector, based on a mesh. //! Creates a simple ITriangleSelector, based on a mesh.
virtual ITriangleSelector* createOctTreeTriangleSelector(IMesh* mesh, virtual ITriangleSelector* createOctTreeTriangleSelector(IMesh* mesh,
ISceneNode* node, s32 minimalPolysPerNode); ISceneNode* node, s32 minimalPolysPerNode);

View File

@ -5,6 +5,7 @@
#include "CTriangleSelector.h" #include "CTriangleSelector.h"
#include "ISceneNode.h" #include "ISceneNode.h"
#include "IMeshBuffer.h" #include "IMeshBuffer.h"
#include "IAnimatedMeshSceneNode.h"
namespace irr namespace irr
{ {
@ -13,7 +14,7 @@ namespace scene
//! constructor //! constructor
CTriangleSelector::CTriangleSelector(const ISceneNode* node) CTriangleSelector::CTriangleSelector(const ISceneNode* node)
: SceneNode(node) : SceneNode(node), AnimatedNode(0)
{ {
#ifdef _DEBUG #ifdef _DEBUG
setDebugName("CTriangleSelector"); setDebugName("CTriangleSelector");
@ -23,12 +24,37 @@ CTriangleSelector::CTriangleSelector(const ISceneNode* node)
//! constructor //! constructor
CTriangleSelector::CTriangleSelector(const IMesh* mesh, const ISceneNode* node) CTriangleSelector::CTriangleSelector(const IMesh* mesh, const ISceneNode* node)
: SceneNode(node) : SceneNode(node), AnimatedNode(0)
{ {
#ifdef _DEBUG #ifdef _DEBUG
setDebugName("CTriangleSelector"); setDebugName("CTriangleSelector");
#endif #endif
createFromMesh(mesh);
}
CTriangleSelector::CTriangleSelector(IAnimatedMeshSceneNode* node)
: SceneNode(reinterpret_cast<ISceneNode*>(node)), AnimatedNode(node)
{
#ifdef _DEBUG
setDebugName("CTriangleSelector");
#endif
if(!AnimatedNode)
return;
IAnimatedMesh * animatedMesh = AnimatedNode->getMesh();
if(!animatedMesh)
return;
IMesh * mesh = animatedMesh->getMesh((s32)AnimatedNode->getFrameNr());
if(mesh)
createFromMesh(mesh);
}
void CTriangleSelector::createFromMesh(const IMesh * mesh)
{
const u32 cnt = mesh->getMeshBufferCount(); const u32 cnt = mesh->getMeshBufferCount();
u32 totalFaceCount = 0; u32 totalFaceCount = 0;
for (u32 j=0; j<cnt; ++j) for (u32 j=0; j<cnt; ++j)
@ -53,6 +79,62 @@ CTriangleSelector::CTriangleSelector(const IMesh* mesh, const ISceneNode* node)
} }
} }
void CTriangleSelector::updateFromMesh(const IMesh* mesh) const
{
if(!mesh)
return;
u32 meshBuffers = mesh->getMeshBufferCount();
u32 triangleCount = 0;
for (u32 i = 0; i < meshBuffers; ++i)
{
IMeshBuffer* buf = mesh->getMeshBuffer(i);
u32 idxCnt = buf->getIndexCount();
const u16* indices = buf->getIndices();
switch (buf->getVertexType())
{
case video::EVT_STANDARD:
{
video::S3DVertex* vtx = (video::S3DVertex*)buf->getVertices();
for (u32 index = 0; index < idxCnt; index += 3)
{
core::triangle3df & tri = Triangles[triangleCount++];
tri.pointA = vtx[indices[index + 0]].Pos;
tri.pointB = vtx[indices[index + 1]].Pos;
tri.pointC = vtx[indices[index + 2]].Pos;
}
}
break;
case video::EVT_2TCOORDS:
{
video::S3DVertex2TCoords* vtx = (video::S3DVertex2TCoords*)buf->getVertices();
for (u32 index = 0; index < idxCnt; index += 3)
{
core::triangle3df & tri = Triangles[triangleCount++];
tri.pointA = vtx[indices[index + 0]].Pos;
tri.pointB = vtx[indices[index + 1]].Pos;
tri.pointC = vtx[indices[index + 2]].Pos;
}
}
break;
case video::EVT_TANGENTS:
{
video::S3DVertexTangents* vtx = (video::S3DVertexTangents*)buf->getVertices();
for (u32 index = 0; index < idxCnt; index += 3)
{
core::triangle3df & tri = Triangles[triangleCount++];
tri.pointA = vtx[indices[index + 0]].Pos;
tri.pointB = vtx[indices[index + 1]].Pos;
tri.pointC = vtx[indices[index + 2]].Pos;
}
}
break;
}
}
}
//! constructor //! constructor
CTriangleSelector::CTriangleSelector(const core::aabbox3d<f32>& box, const ISceneNode* node) CTriangleSelector::CTriangleSelector(const core::aabbox3d<f32>& box, const ISceneNode* node)
@ -66,11 +148,36 @@ CTriangleSelector::CTriangleSelector(const core::aabbox3d<f32>& box, const IScen
} }
void CTriangleSelector::update(void) const
{
if(!AnimatedNode)
return; //< harmless no-op
s32 currentFrame = (s32)AnimatedNode->getFrameNr();
if(currentFrame == LastMeshFrame)
return; //< Nothing to do
LastMeshFrame = currentFrame;
IAnimatedMesh * animatedMesh = AnimatedNode->getMesh();
if(animatedMesh)
{
IMesh * mesh = animatedMesh->getMesh(LastMeshFrame);
if(mesh)
updateFromMesh(mesh);
}
}
//! Gets all triangles. //! Gets all triangles.
void CTriangleSelector::getTriangles(core::triangle3df* triangles, void CTriangleSelector::getTriangles(core::triangle3df* triangles,
s32 arraySize, s32& outTriangleCount, s32 arraySize, s32& outTriangleCount,
const core::matrix4* transform) const const core::matrix4* transform) const
{ {
// Update my triangles if necessary
update();
s32 cnt = Triangles.size(); s32 cnt = Triangles.size();
if (cnt > arraySize) if (cnt > arraySize)
cnt = arraySize; cnt = arraySize;
@ -85,12 +192,6 @@ void CTriangleSelector::getTriangles(core::triangle3df* triangles,
for (s32 i=0; i<cnt; ++i) for (s32 i=0; i<cnt; ++i)
{ {
/*
triangles[i] = Triangles[i];
mat.transformVect(triangles[i].pointA);
mat.transformVect(triangles[i].pointB);
mat.transformVect(triangles[i].pointC);
*/
mat.transformVect( triangles[i].pointA, Triangles[i].pointA ); mat.transformVect( triangles[i].pointA, Triangles[i].pointA );
mat.transformVect( triangles[i].pointB, Triangles[i].pointB ); mat.transformVect( triangles[i].pointB, Triangles[i].pointB );
mat.transformVect( triangles[i].pointC, Triangles[i].pointC ); mat.transformVect( triangles[i].pointC, Triangles[i].pointC );

View File

@ -15,6 +15,7 @@ namespace scene
{ {
class ISceneNode; class ISceneNode;
class IAnimatedMeshSceneNode;
//! Stupid triangle selector without optimization //! Stupid triangle selector without optimization
class CTriangleSelector : public ITriangleSelector class CTriangleSelector : public ITriangleSelector
@ -27,6 +28,10 @@ public:
//! Constructs a selector based on a mesh //! Constructs a selector based on a mesh
CTriangleSelector(const IMesh* mesh, const ISceneNode* node); CTriangleSelector(const IMesh* mesh, const ISceneNode* node);
//! Constructs a selector based on an animated mesh scene node
//!\param node An animated mesh scene node, which must have a valid mesh
CTriangleSelector(IAnimatedMeshSceneNode* node);
//! Constructs a selector based on a bounding box //! Constructs a selector based on a bounding box
CTriangleSelector(const core::aabbox3d<f32>& box, const ISceneNode* node); CTriangleSelector(const core::aabbox3d<f32>& box, const ISceneNode* node);
@ -50,9 +55,22 @@ public:
virtual const ISceneNode* getSceneNodeForTriangle(u32 triangleIndex) const { return SceneNode; } virtual const ISceneNode* getSceneNodeForTriangle(u32 triangleIndex) const { return SceneNode; }
protected: protected:
//! Create from a mesh
virtual void createFromMesh(const IMesh* mesh);
//! Update when the mesh has changed
virtual void updateFromMesh(const IMesh* mesh) const;
//! Update the triangle selector, which will only have an effect if it
//! was built from an animated mesh and that mesh's frame has changed
//! since the last time it was updated.
virtual void update(void) const;
const ISceneNode* SceneNode; const ISceneNode* SceneNode;
mutable core::array<core::triangle3df> Triangles; mutable core::array<core::triangle3df> Triangles;
IAnimatedMeshSceneNode* AnimatedNode;
mutable s32 LastMeshFrame;
}; };
} // end namespace scene } // end namespace scene