Node.Intersect

May 30, 2011 at 8:47 PM

Hi there

I'm trying to determine if a node has been clicked on in the current viewport but I'm not having any luck.  The node is a programmatically built wireframe from a CAD system, for the prototype I'm working on there are thousands of lines in a model.  Possibly I'll work later on making each a separate node, members of a parent node.

But, what I'm trying to do now is determine if the user has picked on the a line (or even coursely the Node itself) but I can't seem to get any result frmo the Node.Intersects method.  I'm passing it the current Viewport (_game.Viewport) and building a ray based on the X and Y positions of the mouse click:

 Ray myray = _game.Viewport.GetPickRay((int)e.GetPosition(this).X, (int)e.GetPosition(this).Y);
 float? result = _wireframe.Intersects(_game.Viewport, myray);

result is always 'null' now matter what I do.

Sorry if this is answered elsewhere, I've searched the documentation and examples but haven't seen this used... if there's something out there, please point me in the right direction.

Peter

Jun 1, 2011 at 2:30 PM

Hi. I am trying to do almost the same thing. I have a geometry that initially is a flat surface. I'm having problems "picking" the vertex on the terrain that corresponds with the mouse position on my viewport.

However, I am able to obtain some data, but something is wrong. I'll share few versions of my code:

Point Position = e.GetPosition(game);
Vector near = new Vector((float)Position.X ,(float)Position.Y,0);
Vector far = new Vector((float)Position.X , (float)Position.Y, 1);

near = game.Camera.Unproject(near,game.Camera.ProjectionMatrix, game.Camera.ViewMatrix, Matrix.Identity);
far = game.Camera.Unproject(far, game.Camera.ProjectionMatrix, game.Camera.ViewMatrix, Matrix.Identity);

var direction = Vector.Normalize(far - near);
Ray ray = new Ray(near, direction);

float BU = 0,BV = 0;
float? distance = null;
int index = 0;
Face face = null;

distance = _gameTerrain.Intersects(game.Viewport, ray, out face, out index, out BU, out BV);

if (distance.HasValue)
{
   ...
}

Using the "distance.HasValue" you can determine wheter your ray intersects the node.

Then, using the face,BU and BV you can determine the exact location of the point where your mouse touches the node in case it's a geometry.

Another way, (I didn't tried it) is to use the distance and interpolate points using "direction". You have something in Balder called "InterpolationPoint"

Of course the way you obtained your Ray is simpler and correct... it should work. Maybe the problem in your code is "this" reference ... (i'm just saying)

Jun 2, 2011 at 5:14 PM

Thanx for the response.

I think the 'this' reference might be causing some issues, you're right.  'this' actually refers here to the page and not the game object itself.

Now, that all being said, both yours and my code results in the same return - null.  My wireframe inherits 'Geometries.Geometry'... it isn't a 'Mesh' per se but a series of lines.  Would this cause any issues you think?

Still working at it tho...

Jun 2, 2011 at 8:02 PM

Oh, only lines... Then this is the problem. Taking a look into the Gemoetry.cs we can see that method Intersect is based on subsequent calls of Ray.IntersectsTriangle(...) where the triangle is provided as three vertices, AND those three vertices are the "corners" of a face. You don't have any face = you don't have intersection.

Still, without allocating faces for your geometry you can check if your pick ray has a chance to intersect it by performing the following test:

var distance = pickRay.Intersects(yourGeometry.BoundingSphere);
if (distance.HasValue)
{
    ...
}
However, the result can be inaccurate and will not provide much info. But this will help you in case you want to... I don't know, translate or rotate your object.

Jun 2, 2011 at 8:13 PM

OK, thanx... I was afraid this would be the issue.  Maybe I can render each of the lines as a separate Geometry and then do the ray.Intersects() while looping through the member objects of the wireframe.

Jun 3, 2011 at 10:08 AM

I don't know how you'll do that (making every line a geometry). But I think I have a better solution for you:

So, you have a geometry that does not have any faces. Still, you have vertices and lines (a wireframe)? Is that correct? or you use Geometry.Render(_viewPort, Balder.Rendering.DetailLevel.Wireframe)?

However, if you have lines allocated in your geometry (_geometry.FullDetailLevel.LineCount > 0) you can write your own method RayIntersectsSegment(Ray ray, Line line). I'm sure you can find a way to test intersection between two lines (in 3D space) determined by two points. Then the next step is to determine if the intersection point is on your segment. Now all you have to do is to call this function for each line until you find a intersection. Or maybe you want to find all of them and choose the closest (then you need to calculate the distance -- no big deal if you have the intersection point).

Hint:

To test if two lines intersects (in 3D world):

  -- you can consider that the lines are in the same plane.

  -- check intersection for two coordinates of your choice (x,y), (x,z), (y,z)

  -- in case they intersect in 2D, check if the third coordinate is the same for both lines

  -- beware to allow some errors in your comparisons (float.Epsilon)

I hope this is clear enough.  I don't have time to write the code for you :)