3D mouse picking

Stephan

Member
Hi guys,

I have a problem:
in my 3d level editor, i am trying to correctly detect the object selected by mouse.
To do this, i first detect the coordinate of the mouse in the 3d space at the ground level (z=0) ;
then i do a kind of raycasting;
  1. I take the camera's lookat "from" coordinates as origin
  2. i take the position of the mouse at the ground , as destination
  3. i compute the distance (sqrt(diffx²+diffy²+diffz²)
  4. then i do a loop from zero to distance, using a small step (like 0.1)
  5. i check the poisiton of the point at distance N like :
    1. px = lerp(fromx,tox,n/distance) (same for py and pz)
    2. i check if that point is inside an object bounding box,
    3. if an object is found, i return it

It work well when the camera is at position x= zero , but when the camera is, by example, at position x = 10 ; or when the object is heigher (position z=2) it is not accruate : it doesent intersect the correct object, but it found the object near from the target.


i am sure that my algorithm is not optimal, i have also tried another aproach:

i construct a normalized vector using the camera position as origin, and the mouse position as target

vecLine = [ diffx/distance,diffy/distance,diffz/distance]
then for each object, i also compute 8 vectors (for each corner), using also the camera as origin, but the corner coordinates at target ; this vector is also normalized using the distance between the camera and the corner coordinates
then i get the minX and maxX, minY maxY, minZ maxZ from all 8 vectors, and i check if the first vector (from the camera to the mouse) is inside the bounds vectors.

this method gives me almost the same result than the first one


do you guys, already implemented 3d mouse picking with game maker, how did you that?
 

Binsk

Member
I actually have a free extension that does just this (includes intersection checking as well).

Since you don't show us any code we can't really say where you might have gone wrong. Still, how I generally implement it is like this:
  • Calculate the inverse projection/view matrix to be able to convert from 2D to 3D coordinates.
  • Convert the 2D mouse position to 3D in two places (one at znear clip and one at zfar clip). Normalizing this gives me a ray that shoots from the camera through the world it the "direction" of the mouse.
  • I use a simple RAY -> AABB (or OBB) intersection script to detect a collision with my shape.
The ray intersection can be pretty fast using easy-to-find formulas. Also, once you have the inverse matrix calculated converting into 3D you can reuse that data over multiple frames (until the camera moves).
 

Stephan

Member
my problem is solved,
the problem was because i used a grid size, and i didn't take it in account in my formula:


for info, there is how i did:

GML:
//the camera view origin
var fx = obj_camera.xfrom;
var fy = obj_camera.yfrom;
var fz = obj_camera.zfrom;

//the mouse target position
var tx = obj_camera.mouse_3D[0];
var ty = obj_camera.mouse_3D[1];
var tz = obj_camera.mouse_3D[2];


//a vector that represent the line from the camera to the target mouse position
var lineVec = get_vect_normal([tx-fx,ty-fy,tz-fz]);
var meshes = global.mesh_list;
var nbr_mesh = ds_list_size(meshes);
var nearest_obj = undefined;
var nearest_distance = 999999;
if (nbr_mesh>0)
{

    for(var j = 0;j<nbr_mesh;j++)
    {
        var m = meshes[| j];

        //for each corner, i get a vector from the camera origin to the corner position
        var v1 = get_vect_normal([m.x1-fx,m.y1-fy,m.z1-fz]);
        var v2 = get_vect_normal([m.x2-fx,m.y1-fy,m.z1-fz]);
        var v3 = get_vect_normal([m.x2-fx,m.y2-fy,m.z1-fz]);
        var v4 = get_vect_normal([m.x1-fx,m.y2-fy,m.z1-fz]);
     
        var v5 = get_vect_normal([m.x1-fx,m.y1-fy,m.z2-fz]);
        var v6 = get_vect_normal([m.x2-fx,m.y1-fy,m.z2-fz]);
        var v7 = get_vect_normal([m.x2-fx,m.y2-fy,m.z2-fz]);
        var v8 = get_vect_normal([m.x1-fx,m.y2-fy,m.z2-fz]);
     
        //then i get the bounds of the vector
        var _min_x = min(v1[0],v2[0],v3[0],v4[0],v5[0],v6[0],v7[0],v8[0]);
        var _max_x = max(v1[0],v2[0],v3[0],v4[0],v5[0],v6[0],v7[0],v8[0]);
        var _min_y = min(v1[1],v2[1],v3[1],v4[1],v5[1],v6[1],v7[1],v8[1]);
        var _max_y = max(v1[1],v2[1],v3[1],v4[1],v5[1],v6[1],v7[1],v8[1]);
        var _min_z = min(v1[2],v2[2],v3[2],v4[2],v5[2],v6[2],v7[2],v8[2]);
        var _max_z = max(v1[2],v2[2],v3[2],v4[2],v5[2],v6[2],v7[2],v8[2]);
     
        //and i check if the line from the camera to the mouse, is inside the lines from the camera to the bounds
        if (lineVec[0]>=_min_x && lineVec[0]<=_max_x &&
            lineVec[1]>=_min_y && lineVec[1]<=_max_y &&
            lineVec[2]>=_min_z && lineVec[2]<=_max_z)
        {
            var adx = (m.x)-fx;
            var ady = m.y-fy;
            var adz = m.z-fz;
            var dist = sqrt(adx*adx+ady*ady+adz*adz);
            //i get the nearest object
            if (dist<nearest_distance)
            {
                nearest_obj =m;
                nearest_distance = dist;
            }
        }
    }

}

return nearest_obj;
 
Last edited:
Top