Picking and finding the point in 3D

Hi,

I am trying to develop a picking functionality, and I am based on Sinisa Stokic

I want to go deeper and get the 3D point touched, so I need to check if that point belongs or not to the current face in the loop.

Then, getting the points belonging to the shape, I will get the nearest to the camera.

But... BRepClass_FaceClassifier is lying to me :) or I don't know how to use it properly, because State is always returning TopAbs_OUT.

As you can see in the picture, line is getting well, and some points are over some faces. Any advise?
Free Image Hosting at www.ImageShack.us

QuickPost

This is my full code (Only rest to order the point and get the nearest one). Image has been taken commenting if clause to draw points in any case:

gp_Pnt COCC::PickPoint(long x, long y)
{
V3d_Coordinate xEye, yEye, zEye, xAt, yAt, zAt;

m_view->Eye(xEye, yEye, zEye);
m_view->At(xAt, yAt, zAt);
gp_Pnt EyePoint(xEye, yEye, zEye);
gp_Pnt AtPoint(xAt, yAt, zAt);

gp_Vec EyeVector(EyePoint, AtPoint);
gp_Dir EyeDir(EyeVector);

gp_Pln PlaneOfView = gp_Pln(AtPoint, EyeDir);

Standard_Real theX, theY, theZ;
m_view->Convert(x, y, theX, theY, theZ);
gp_Pnt ConvertedPoint (theX, theY, theZ);

gp_Pnt2d ConvertedPointOnPlane = ProjLib::Project(PlaneOfView, ConvertedPoint);

gp_Pnt ResultPoint = ElSLib::Value(ConvertedPointOnPlane.X(), ConvertedPointOnPlane.Y(), PlaneOfView);

GC_MakeLine line(ResultPoint, EyeDir);
TopExp_Explorer exp;
TopAbs_State aState;

for (exp.Init(m_pickingShape, TopAbs_FACE); exp.More(); exp.Next())
{
TopoDS_Face face = TopoDS::Face(exp.Current());
BRepAdaptor_Surface surface(face);
const GeomAdaptor_Surface& geomAdapSurf = surface.Surface();
const Handle(Geom_Surface)& geomSurf = geomAdapSurf.Surface();

GeomAPI_IntCS inCS;
inCS.Perform(line, geomSurf);
if (inCS.IsDone())
{
if (inCS.NbPoints()!=0)
{
ResultPoint = gp_Pnt(inCS.Point(1).XYZ());
BRepClass_FaceClassifier aClassifier(face, ResultPoint, Precision::Confusion());
aState = aClassifier.State();
if ((aState==TopAbs_ON) || (aState == TopAbs_IN))
{
COCCVertex* a = new COCCVertex(ResultPoint);
ShowVertex(a);
}
}
}
}
return ResultPoint;
}

arkoala's picture

Any hint to make working this code?
Why is always returning TopAbs_OUT if I am watching some of them are over the face?

Merry Christmas to everybody!

BRepClass_FaceClassifier aClassifier(face, shapePoint, Precision::Confusion());
aState = aClassifier.State();
if ((aState==TopAbs_ON) || (aState == TopAbs_IN))
{
if (resultPoint.Distance(EyePoint) > shapePoint.Distance(shapePoint))
{
resultPoint = shapePoint;
}
}

Paul Jimenez's picture

I don't really get what you're trying to achieve. Do you want to get the point closest to the "eye" that belongs to a face in a model by clicking in the view? Something like: "if I shoot here, which face will I hit first"?

arkoala's picture

Yes, that was: What am i touching if I click over the screen?

I wanted to replay myself yesterday, but forum wasn't working.

I found the problem reading the code.
If I use BRepClass_FaceClassifier with gp_Pnt constructor, that point is no longer used.
Instead of that I used gp_Pnt2d constructor, so BRepClass_FaceClassifier worked well. I get now if a point belong to a face or not.

This is the code for the picking purpose. It only rest a boolean variable returning if the point has a real value or user has clicked over the "air" (nothing touch)
I hope to be useful in the future for newbies like me.

gp_Pnt PickPoint(long x, long y)
{
gp_Pnt resultPoint;
V3d_Coordinate xEye, yEye, zEye, xAt, yAt, zAt;

m_view->Eye(xEye, yEye, zEye);
m_view->At(xAt, yAt, zAt);
gp_Pnt EyePoint(xEye, yEye, zEye);
gp_Pnt AtPoint(xAt, yAt, zAt);

gp_Vec EyeVector(EyePoint, AtPoint);
gp_Dir EyeDir(EyeVector);

gp_Pln PlaneOfView = gp_Pln(AtPoint, EyeDir);

Standard_Real theX, theY, theZ;
m_view->Convert(x, y, theX, theY, theZ);
gp_Pnt ConvertedPoint (theX, theY, theZ);

gp_Pnt2d ConvertedPointOnPlane = ProjLib::Project(PlaneOfView, ConvertedPoint);

gp_Pnt shapePoint = ElSLib::Value(ConvertedPointOnPlane.X(), ConvertedPointOnPlane.Y(), PlaneOfView);
resultPoint = shapePoint;//Initialize with a very far point from the camera

GC_MakeLine line(shapePoint, EyeDir);
TopExp_Explorer exp;
TopAbs_State aState;

for (exp.Init(m_pickingShape, TopAbs_FACE); exp.More(); exp.Next())
{
TopoDS_Face face = TopoDS::Face(exp.Current());
BRepAdaptor_Surface surface(face);

const GeomAdaptor_Surface& geomAdapSurf = surface.Surface();
const Handle(Geom_Surface)& geomSurf = geomAdapSurf.Surface();

GeomAPI_IntCS inCS;
inCS.Perform(line, geomSurf);
if (inCS.IsDone())
{
if (inCS.NbPoints()!=0)
{
shapePoint = gp_Pnt(inCS.Point(1).XYZ());

ShapeAnalysis_Surface shapeAnalysis(geomSurf);
gp_Pnt2d shapePoint2D = shapeAnalysis.ValueOfUV(shapePoint, Precision::Confusion());
BRepClass_FaceClassifier aClassifier(face, shapePoint2D, Precision::Confusion());
aState = aClassifier.State();
if ((aState==TopAbs_ON) || (aState == TopAbs_IN))
{
if (resultPoint.Distance(EyePoint) > shapePoint.Distance(shapePoint))
{
resultPoint = shapePoint;
}
}
}
}
}
return resultPoint;
}

Super Dan's picture

Thank you! your work help me a lot!

Stephane Routelous's picture

I faced mostly a problem of performances using this kind of code.
The solution I'm using is only based on the triangulation of the shape.
I'm using an octree (16 levels iirc) to store the triangles and basic box/line intersection until I'm at the lowest level, then loop on the triangles to compute triangle/line intersections
On very complex shapes, I have the intersection on the mesh in ~50ms. If I need the real intersection on the model, it's a little slower (but not so much as I'm using the UV coordinates on the triangles).
Of course, it uses memory and time at the initialization to mesh and build the octree.

HTH,

Stephane

arkoala's picture

I was trying that way because I only manage a shape with no more than 20 faces. Also, I don't need a very fast response because user clicks are slower than picking algorithm. It is almost brute force attack.

I will need that kind of solution when I manage collision detection. Then I will need not only a fast solution but also a precission one. See you then xD.