Dans le cadre d’un de mes projets, un utilisateur devait pouvoir cliquer sur une face et une arrête d’un objet, non aligné avec les axes (x,y,z), donc avec pour moi l’impossibilité d’utiliser les Bounding Box proposée par Irrlicht.
Il fallait alors lancer le rayon depuis la souris à chaque frame, et déterminer quelle face traversée par ce rayon est la plus proche de la caméra.
Avec l’aide de rogerborg du forum d’Irrlicht, qui me donna la solution pour les arrêtes, j’ai enfin réussi à trouver le code nécessaire pour la scène node face qui permet cela.
/!*
*\author : Mathieu PICCIN
*\date : SEPTEMBER 2008
*\brief : a face scene node with selection from mouse
**/
#include
#include
using namespace irr;
#pragma comment(lib, "Irrlicht.lib")
class SelectableFacesNode : public scene::ISceneNode
{
core::aabbox3df Box;
std::vector Faces;
std::vector corners;
//core::array Edges;
core::array EdgeUnitVectors;
video::SMaterial Material;
int SelectedFace;
public:
SelectableFacesNode(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id)
: scene::ISceneNode(parent, mgr, id)
{
SelectedFace = -1;
Material.Wireframe = false;
Material.Lighting = false;
// Material.TextureLayer[0].Texture[0].
corners.push_back(core::vector3df(0, 14, 0));
corners.push_back(core::vector3df(-10, 0, -10));
corners.push_back(core::vector3df(10, 0, -10));
corners.push_back(core::vector3df(0, 0, 14));
for(int corner1 = 0; corner1 < 4; ++corner1){
for(int corner2 = corner1 + 1; corner2 < 4; ++corner2)
{
for(int corner3 = corner2+1; corner3 <4; ++corner3)
{
const core::triangle3df face(corners[corner1], corners[corner2], corners[corner3]);
Faces.push_back(face);
}
}
}
/*Vertices[0] = video::S3DVertex(0,0,10, 1,1,0,video::SColor(255,0,255,255),0,1);
Vertices[1] = video::S3DVertex(10,0,-10, 1,0,0,video::SColor(255,255,0,255),1,1);
Vertices[2] = video::S3DVertex(0,20,0, 0,1,1,video::SColor(255,255,255,0),1,0);
Vertices[3] = video::S3DVertex(-10,0,-10, 0,0,1,video::SColor(255,0,255,0),0,0);*/
Box.reset(corners[0]);
for (s32 i=1; iregisterNodeForRendering(this);
ISceneNode::OnRegisterSceneNode();
}
virtual void render()
{
u16 indices[] = { 0,2,3, 2,1,3, 1,0,3, 2,0,1 };
video::IVideoDriver* driver = SceneManager->getVideoDriver();
driver->setMaterial(Material);
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
for(u32 face = 0; face draw3DTriangle(Faces[face], video::SColor(255, 0, 255, 0));
else
driver->draw3DTriangle(Faces[face], video::SColor(255, 0, 0, 255));
}
}
virtual const core::aabbox3d& getBoundingBox() const
{
return Box;
}
virtual u32 getMaterialCount() const
{
return 1;
}
virtual video::SMaterial& getMaterial(u32 i)
{
return Material;
}
void testLineIntersectionWithEdges(const core::line3df & line)
{
core::line3df localLine(line);
core::matrix4 inverseTransform;
if(!AbsoluteTransformation.getInverse(inverseTransform))
return;
inverseTransform.transformVect(localLine.start);
inverseTransform.transformVect(localLine.end);
const core::vector3df lineUnit = localLine.getVector().normalize();
const f32 VirtualEdgeThicknessSq = 6.f * 3.f; // Thicken up the edge
f32 closestToLineSq = VirtualEdgeThicknessSq;
SelectedFace = -1;
for(u32 face = 0; face < 4; ++face)
{
/*core::vector3df normal = lineUnit.crossProduct(EdgeUnitVectors[edge]);
normal = EdgeUnitVectors[edge].crossProduct(normal);*/
core::plane3df plane = Faces.at(face).getPlane();
core::vector3df intersection;
if(Faces.at(face).getIntersectionWithLine(localLine.start, lineUnit, intersection))
{
const core::vector3df nearestPoint = Faces[face].closestPointOnTriangle(intersection);
const f32 distanceToLineSq = intersection.getDistanceFromSQ(nearestPoint);
if(distanceToLineSq <= closestToLineSq)
{
closestToLineSq = distanceToLineSq;
SelectedFace = face;
}
}
}
}
};
int main()
{
IrrlichtDevice *device =
createDevice(video::EDT_OPENGL, core::dimension2d(640, 480), 16, false);
if (device == 0)
return 1; // could not create selected driver.
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
smgr->addCameraSceneNode(0, core::vector3df(0,0,-40), core::vector3df(0,0,0));
SelectableFacesNode *myNode =
new SelectableFacesNode(smgr->getRootSceneNode(), smgr, 666);
scene::ISceneNodeAnimator* anim =
smgr->createRotationAnimator(core::vector3df(0.3f, 0, 0.3f));
myNode->addAnimator(anim);
anim->drop();
while(device->run())
{
core::line3df cursorRay = smgr->getSceneCollisionManager()->getRayFromScreenCoordinates(device->getCursorControl()->getPosition());
myNode->testLineIntersectionWithEdges(cursorRay);
driver->beginScene(true, true, video::SColor(0,100,100,100));
smgr->drawAll();
driver->endScene();
}
myNode->drop();
device->drop();
return 0;
}