- 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
- 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.
- 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
In this tutorial, I will show how to detect collisions 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.
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 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.
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 a quake 3 level, as per tutorial 2.
*/
#include <irrlicht.h>
#include <iostream>
@ -20,6 +19,22 @@ using namespace irr;
#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()
{
// let user select driver type
@ -61,10 +76,9 @@ int main()
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->addOctTreeSceneNode(q3levelmesh->getMesh(0));
q3node->setID(0); // Make it an invalid target for bounding box collision
q3node = smgr->addOctTreeSceneNode(q3levelmesh->getMesh(0), 0, IDFlag_IsPickable);
/*
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(
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 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
special animator to the camera: A Collision Response animator. This
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,
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 more for collision detection, anything is done automatically,
all other collision detection code below is for picking. And please
anything more for collision detection, anything is 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, really easy.
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
@ -132,9 +147,8 @@ int main()
// 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.
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->setID(0); // Make it an invalid target for bounding box collision
if (selector)
{
@ -142,165 +156,154 @@ int main()
selector, camera, core::vector3df(30,50,30),
core::vector3df(0,-10,0),
core::vector3df(0,50,0));
selector->drop(); // As soon as we're done with the selector, drop it.
camera->addAnimator(anim);
anim->drop();
anim->drop(); // And likewise, drop the animator when we're done referring to it.
}
/*
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
// 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 billboard
// Add the 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->setMaterialFlag(video::EMF_ZBUFFER, false);
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
// that we can see the same boxes that the scene collision manager is
// using to perform the getSceneNodeFromCameraBB() check below.
// 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("../../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;
material.setTexture(0, driver->getTexture("../../media/faerie2.bmp"));
material.Lighting = true;
node->getMaterial(0) = material;
scene::IAnimatedMeshSceneNode* node = 0;
scene::IAnimatedMesh* faerie = smgr->getMesh("../../media/faerie.md2");
// 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);
node->setTriangleSelector(selector);
selector->drop(); // We're done with this selector, so drop it now.
if (faerie)
{
node = smgr->addAnimatedMeshSceneNode(faerie);
node->setPosition(core::vector3df(-70,0,-90));
node->setMD2Animation(scene::EMAT_RUN);
node->getMaterial(0) = material;
node->setDebugDataVisible(scene::EDS_BBOX_ALL);
node = smgr->addAnimatedMeshSceneNode(faerie);
node->setPosition(core::vector3df(-70,0,-30));
node->setMD2Animation(scene::EMAT_SALUTE);
node->getMaterial(0) = material;
node->setDebugDataVisible(scene::EDS_BBOX_ALL);
// This X files uses skeletal animation, but without skinning.
node = smgr->addAnimatedMeshSceneNode(smgr->getMesh("../../media/dwarf.x"),
0,
IDFlag_IsPickable | IDFlag_IsHighlightable);
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();
node = smgr->addAnimatedMeshSceneNode(faerie);
node->setPosition(core::vector3df(-70,0,-60));
node->setMD2Animation(scene::EMAT_JUMP);
node->getMaterial(0) = material;
node->setDebugDataVisible(scene::EDS_BBOX_ALL);
}
// 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.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),
video::SColorf(1.0f,1.0f,1.0f,1.0f),
600.0f);
light->setID(0); // Make it an invalid target for bounding box collision
/*
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;
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();
int lastFPS = -1;
while(device->run())
if (device->isWindowActive())
{
driver->beginScene(true, true, 0);
smgr->drawAll();
/*
After we've drawn the whole scene with 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;
const scene::ISceneNode* hitNode;
if (smgr->getSceneCollisionManager()->getCollisionPoint(
line, selector, intersection, tri, hitNode))
// Unlight any currently highlighted scene node
if (highlightedSceneNode)
{
bill->setPosition(intersection);
driver->setTransform(video::ETS_WORLD, core::matrix4());
driver->setMaterial(material);
driver->draw3DTriangle(tri, video::SColor(0,255,0,0));
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;
/*
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.
*/
// Tracks the current intersection point with the level or a mesh
core::vector3df intersection;
// Used to show with triangle has been hit
core::triangle3df hitTriangle;
selectedSceneNode =
smgr->getSceneCollisionManager()->getSceneNodeFromCameraBB(camera, 1);
// 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.
// 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 (lastSelectedSceneNode)
lastSelectedSceneNode->setMaterialFlag(video::EMF_LIGHTING, true);
// If the ray hit anything, move the billboard to the collision position and draw
// the triangle that was hit.
if(selectedSceneNode)
{
bill->setPosition(intersection);
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.
*/
// We need to reset the transform before doing our own rendering.
driver->setTransform(video::ETS_WORLD, core::matrix4());
driver->setMaterial(material);
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.
driver->endScene();
int fps = driver->getFPS();
@ -317,7 +320,6 @@ int main()
}
}
selector->drop();
device->drop();
return 0;

View File

@ -121,29 +121,29 @@ namespace scene
virtual ISceneNode* getSceneNodeFromScreenCoordinatesBB(const core::position2d<s32> & pos,
s32 idBitMask=0, bool bNoDebugObjects = false) = 0;
//! Get 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
scene node.
//! 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 scene node.
\param ray: Line with witch collisions are tested.
\param idBitMask: Only scene nodes with an id with bits set
like in this mask will be tested. If the BitMask is 0, this
feature is disabled.
\param bNoDebugObjects: Doesn't take debug objects into account
when true. These are scene nodes with IsDebugObject() = true.
\return Scene node nearest to ray.start, which collides with
the ray and matches the idBitMask, if the mask is not null. If
no scene node is found, 0 is returned. */
virtual ISceneNode* getSceneNodeFromRayBB(const core::line3d<f32> & ray,
s32 idBitMask=0, bool bNoDebugObjects = false) = 0;
\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 bNoDebugObjects: Doesn't take debug objects into account when true. These
are scene nodes with IsDebugObject() = true.
\return Returns the scene node nearest to ray.start, which collides with the
ray and matches the idBitMask, if the mask is not null. If no scene
node is found, 0 is returned. */
virtual ISceneNode* getSceneNodeFromRayBB(const core::line3d<f32> ray,
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
the view target position, and all scene nodes are tested
against this ray. The collision tests are done using a bounding
box for each scene node.
\param camera: Camera from which the ray is casted.
\param idBitMask: Only scene nodes with an id with bits set
like in this mask will be tested. If the BitMask is 0, this
\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.
feature is disabled.
\param bNoDebugObjects: Doesn't take debug objects into account
when true. These are scene nodes with IsDebugObject() = true.
@ -152,6 +152,41 @@ namespace scene
no scene node is found, 0 is returned. */
virtual ISceneNode* getSceneNodeFromCameraBB(ICameraSceneNode* camera,
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. */
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.
/** Triangle selectors
can be used for doing collision detection. Every time when triangles are

View File

@ -57,9 +57,9 @@ ISceneNode* CSceneCollisionManager::getSceneNodeFromScreenCoordinatesBB(
//! Returns the nearest scene node which collides with a 3d ray and
//! which id matches a bitmask.
ISceneNode* CSceneCollisionManager::getSceneNodeFromRayBB(const core::line3d<f32> & ray,
s32 idBitMask,
bool bNoDebugObjects)
ISceneNode* CSceneCollisionManager::getSceneNodeFromRayBB(const core::line3d<f32> ray,
s32 idBitMask,
bool bNoDebugObjects)
{
ISceneNode* best = 0;
f32 dist = FLT_MAX;
@ -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
//! 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
//! which id matches a bitmask.
virtual ISceneNode* getSceneNodeFromRayBB(const core::line3d<f32> & ray,
s32 idBitMask=0,
bool bNoDebugObjects = false);
virtual ISceneNode* getSceneNodeFromRayBB(const core::line3d<f32> ray,
s32 idBitMask=0, bool bNoDebugObjects = false);
//! Returns the scene node, at which the overgiven camera is looking at and
//! which id matches the bitmask.
@ -69,6 +68,17 @@ namespace scene
virtual core::position2d<s32> getScreenCoordinatesFrom3DPosition(
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:
//! recursive method for going through all scene nodes
@ -79,6 +89,17 @@ namespace scene
f32& outbestdistance,
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
{
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.
ITriangleSelector* CSceneManager::createTriangleSelectorFromBoundingBox(ISceneNode* node)
{

View File

@ -327,6 +327,13 @@ namespace scene
//! Creates a simple ITriangleSelector, based on a mesh.
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.
virtual ITriangleSelector* createOctTreeTriangleSelector(IMesh* mesh,
ISceneNode* node, s32 minimalPolysPerNode);

View File

@ -5,6 +5,7 @@
#include "CTriangleSelector.h"
#include "ISceneNode.h"
#include "IMeshBuffer.h"
#include "IAnimatedMeshSceneNode.h"
namespace irr
{
@ -13,7 +14,7 @@ namespace scene
//! constructor
CTriangleSelector::CTriangleSelector(const ISceneNode* node)
: SceneNode(node)
: SceneNode(node), AnimatedNode(0)
{
#ifdef _DEBUG
setDebugName("CTriangleSelector");
@ -23,12 +24,37 @@ CTriangleSelector::CTriangleSelector(const ISceneNode* node)
//! constructor
CTriangleSelector::CTriangleSelector(const IMesh* mesh, const ISceneNode* node)
: SceneNode(node)
: SceneNode(node), AnimatedNode(0)
{
#ifdef _DEBUG
setDebugName("CTriangleSelector");
#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();
u32 totalFaceCount = 0;
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
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.
void CTriangleSelector::getTriangles(core::triangle3df* triangles,
s32 arraySize, s32& outTriangleCount,
const core::matrix4* transform) const
{
// Update my triangles if necessary
update();
s32 cnt = Triangles.size();
if (cnt > arraySize)
cnt = arraySize;
@ -85,12 +192,6 @@ void CTriangleSelector::getTriangles(core::triangle3df* triangles,
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].pointB, Triangles[i].pointB );
mat.transformVect( triangles[i].pointC, Triangles[i].pointC );

View File

@ -15,6 +15,7 @@ namespace scene
{
class ISceneNode;
class IAnimatedMeshSceneNode;
//! Stupid triangle selector without optimization
class CTriangleSelector : public ITriangleSelector
@ -27,6 +28,10 @@ public:
//! Constructs a selector based on a mesh
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
CTriangleSelector(const core::aabbox3d<f32>& box, const ISceneNode* node);
@ -50,9 +55,22 @@ public:
virtual const ISceneNode* getSceneNodeForTriangle(u32 triangleIndex) const { return SceneNode; }
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;
mutable core::array<core::triangle3df> Triangles;
IAnimatedMeshSceneNode* AnimatedNode;
mutable s32 LastMeshFrame;
};
} // end namespace scene