Commit 5478f805 authored by Rogerborg's avatar Rogerborg

https://sourceforge.net/tracker2/?func=detail&aid=1797487&group_id=74339&atid=540678

 - 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
parent 78f9dee7
......@@ -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.
......
This diff is collapsed.
......@@ -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;
//! Get the scene node, which the overgiven camera is looking at and whose id matches the bitmask.
\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 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;
};
......
......@@ -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
......
......@@ -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.
......
......@@ -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;
......
......@@ -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)
{
......
......@@ -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);
......
......@@ -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 );
......
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment