views:

923

answers:

2

Looking to do classic OpenGL mouse picking in ES. I'd prefer not to use third party libs, GLU ports and OpenGL name stacks, etc, are out. This pretty much leaves inverse view transformation and ray intersection, correct?

I've gotten pretty far with the help of: http://trac.bookofhook.com/bookofhook/trac.cgi/wiki/MousePicking http://eigenclass.blogspot.com/2008/10/opengl-es-picking-using-ray-boundingbox.html

. . .but I'm not there yet. This also reeks of THERE MUST BE AN EASIER WAY!!

Here is some code:

    -(void)handleTouch:(CGPoint)point {
    GLfloat width = backingWidth;
    GLfloat height = backingHeight;
    GLfloat x = point.x;
    GLfloat y = point.y;
    GLfloat z = 0.0f;

    //viewport -> normalized dev coord -> clip
    GLfloat n[] = {
        2 * x / width - 1,
        2 * y / height,
        2 * z - 1,
        1
    };

    float fov = 45.0f * (M_PI / 180.0f);
    float near = 0.01, far = 10.0f;
    float aspect = (float)backingWidth / (float)backingHeight;
    float top = tan(fov) * near;
    //float bottom = -top;
    //float left = aspect * bottom;
    float right = aspect * top;

    //I'm a viewing volume symmetric projection matrix
    GLfloat P[] = {
        near / right, 0, 0, 0,
        0, near / top, 0, 0,
        0, 0, -(far + near) / (far - near), (-2 * far * near) / (far - near),
        0, 0, -1, 0
    };

    GLfloat Pminus1[] = {
        1/P[0], 0, 0, 0,
        0, 1/P[5], 0, 0,
        0, 0, 0, 1/P[14],
        0, 0, 1/P[11], -(P[10]/ (P[11]*P[14]))
    };

    //clip -> view
    GLfloat v[] = {
        Pminus1[0] * n[0] + Pminus1[1] * n[1] + Pminus1[2] * n[2] + Pminus1[3] * n[3],
        Pminus1[4] * n[0] + Pminus1[5] * n[1] + Pminus1[6] * n[2] + Pminus1[7] * n[3],
        Pminus1[8] * n[0] + Pminus1[9] * n[1] + Pminus1[10] * n[2] + Pminus1[11] * n[3],
        Pminus1[12] * n[0] + Pminus1[13] * n[1] + Pminus1[14] * n[2] + Pminus1[15] * n[3]
    };

    //view -> world
    GLfloat Rt[] = {
        mv[0], mv[4], mv[8],
        mv[1], mv[5], mv[9],
        mv[2], mv[6], mv[10]
    };

    GLfloat tPrime[] = {
        Rt[0] * mv[3] + Rt[1] * mv[7] + Rt[2] * mv[11],
        Rt[3] * mv[3] + Rt[4] * mv[7] + Rt[5] * mv[11],
        Rt[6] * mv[3] + Rt[7] * mv[7] + Rt[8] * mv[11]
    };

    GLfloat Mminus1[] = {
        Rt[0], Rt[1], Rt[2], -(tPrime[0]),
        Rt[3], Rt[4], Rt[5], -(tPrime[1]),
        Rt[6], Rt[7], Rt[8], -(tPrime[2]),
        0, 0, 0, 1
    };

    //point in world space
    GLfloat w[] = {
        Mminus1[0] * v[0] + Mminus1[1] * v[1] + Mminus1[2] * v[2] + Mminus1[3] * v[3],
        Mminus1[4] * v[0] + Mminus1[5] * v[1] + Mminus1[6] * v[2] + Mminus1[7] * v[3],
        Mminus1[8] * v[0] + Mminus1[9] * v[1] + Mminus1[10] * v[2] + Mminus1[11] * v[3],
        Mminus1[12] * v[0] + Mminus1[13] * v[1] + Mminus1[14] * v[2] + Mminus1[15] * v[3]
    };

    //r = a + t(w - a)
    GLfloat a[] = {0.0f, -0.1f, 0.0f};
    GLfloat wminusa[] = {w[0] - a[0], w[1] - a[1], w[2] - a[2]};

    vector[0] = a[0];
    vector[1] = a[1],
    vector[2] = a[2];
    vector[3] = w[0];
    vector[4] = w[1];
    vector[5] = -10.0f;

    //3 non-colinear points on the plane 
    GLfloat p1[] = {rect.origin.x, rect.origin.y, 0};
    GLfloat p2[] = {rect.origin.x + rect.size.width, rect.origin.y, 0};
    GLfloat p3[] = {rect.origin.x + rect.size.width, rect.origin.y + rect.size.height, 0};

    //location plane normal vector, Ax + By + Cz + D = 0
    GLfloat lp[] = {
        p1[1] * (p2[2] - p3[2]) + p2[1] * (p3[2] - p1[2]) + p3[1] * (p1[2] - p2[2]),
        p1[2] * (p2[0] - p3[0]) + p2[2] * (p3[0] - p1[0]) + p3[2] * (p1[0] - p2[0]),
        p1[0] * (p2[1] - p3[1]) + p2[0] * (p3[1] - p1[1]) + p3[0] * (p1[1] - p2[1]),
        -(p1[0] * (((p2[1] * p3[2]) - (p3[1] * p2[2]))) + p2[0] * (((p3[1] * p1[2]) - (p1[1] * p3[2]))) + p3[0] * (((p1[1] * p2[2]) - (p2[1] * p1[2]))))
    };

    GLfloat PnRd = (lp[0] * wminusa[0]) + (lp[1] * wminusa[1]) + (lp[2] * wminusa[2]);
    if(PnRd != 0) {
        GLfloat PnR0D = -((lp[0] * a[0]) + (lp[1] * a[1]) + (lp[2] * a[2]) + lp[3]);
        if(PnR0D != 0) {
            GLfloat t = PnR0D / PnRd;
            if(t >= 0) {
                GLfloat p[] = {
                    a[0] + wminusa[0] * t,
                    a[1] + wminusa[1] * t,
                    a[2] + wminusa[2] * t
                };
                if(p[0] > rect.origin.x &&
                   p[0] < rect.origin.x + rect.size.width &&
                   p[1] > rect.origin.y &&
                   p[1] < rect.origin.y + rect.size.height)
                    NSLog(@"BOOM!!!");
            }
        }
    }
}
A: 

I dun fixed it:

-(void)view2WorldPoint:(CGPoint)point :(GLfloat*)worldPoint {
// this is the inverse translation of the modelview
GLfloat width = (GLfloat)backingWidth;
GLfloat height = (GLfloat)backingHeight;

float clickX = point.x;
float clickY = point.y;
float clickZ = 0.0f;

NSLog(@"click point : x = %f, y = %f, z = %f", clickX, clickY, clickZ);

//  NSLog(@"Me : x = %f, y = %f, z = %f", a[0], a[1], a[2]);
//  NSLog(@"Dev : x = %f, y = %f, z = %f", squareX, squareY, squareZ);

//viewport -> normalized device coord -> clip
GLfloat n[] = {
    2 * clickX / width - 1,
    2 * (480-clickY) / height - 1,
    2 * clickZ - 1,
    1
};
//  NSLog(@"Obj : x = %f, y = %f, z = %f", rect.origin.x, rect.origin.y, -0.5);

// NSLog(@"N : x = %f, y = %f, z = %f", n[0], n[1], n[2]);

//I'm a viewing volume symmetric projection matrix

// GLfloat P[] = { // near / right, 0, 0, 0, // 0, near / top, 0, 0, // 0, 0, -(far + near) / (far - near), (-2 * far * near) / (far - near), // 0, 0, -1, 0 // }; GLfloat P[16]; glGetFloatv(GL_PROJECTION_MATRIX, P); // [self dumpMatrix:P :@"P"];

GLfloat Pminus1[] = {
    1/P[0], 0, 0, 0,
    0, 1/P[5], 0, 0,
    0, 0, 0, 1/P[11],
    0, 0, 1/P[14], -(P[10]/ (P[11]*P[14]))
};

// [self dumpMatrix:Pminus1 :@"P-1"];

//clip -> view
GLfloat v[] = {
    (Pminus1[0] * n[0]) + (Pminus1[1] * n[1]) + (Pminus1[2]  * n[2]) + (Pminus1[3] * n[3]),
    (Pminus1[4] * n[0]) + (Pminus1[5] * n[1]) + (Pminus1[6]  * n[2]) + (Pminus1[7] * n[3]),
    (Pminus1[8] * n[0]) + (Pminus1[9] * n[1]) + (Pminus1[10] * n[2]) + (Pminus1[11] * n[3]),
    (Pminus1[12] * n[0]) + (Pminus1[13] * n[1]) + (Pminus1[14] * n[2]) + (Pminus1[15] * n[3])
};

// NSLog(@"v = [%f, %f, %f, %f]", v[0], v[1], v[2], v[3]);

// [self dumpMatrix:mv :@"mv"];

//view -> world
GLfloat Rt[] = {
    mv[0], mv[4], -mv[8],
    mv[1], mv[5], -mv[9],
    -mv[2], -mv[6], mv[10]
};

// NSLog(@"Rt0 = [%f, %f, %f]", Rt[0], Rt[1], Rt[2]); // NSLog(@"Rt1 = [%f, %f, %f]", Rt[3], Rt[4], Rt[5]); // NSLog(@"Rt2 = [%f, %f, %f]", Rt[6], Rt[7], Rt[8]);

GLfloat tPrime[] = {
    Rt[0] * mv[12] + Rt[1] * mv[13] + Rt[2] * mv[14],
    Rt[3] * mv[12] + Rt[4] * mv[13] + Rt[5] * mv[14],
    Rt[6] * mv[12] + Rt[7] * mv[13] + Rt[8] * mv[14]
};

// NSLog(@"tPrime = [%f, %f, %f]", tPrime[0], tPrime[1], tPrime[2]);

GLfloat Mminus1[] = {
    Rt[0], Rt[1], Rt[2], -(tPrime[0]),
    Rt[3], Rt[4], Rt[5], -(tPrime[1]),
    Rt[6], Rt[7], Rt[8], -(tPrime[2]),
    0, 0, 0, 1
};

//point in world space
GLfloat w[] = {
    Mminus1[0] * v[0] + Mminus1[1] * v[1] + Mminus1[2] * v[2] + Mminus1[3] * v[3],
    Mminus1[4] * v[0] + Mminus1[5] * v[1] + Mminus1[6] * v[2] + Mminus1[7] * v[3],
    Mminus1[8] * v[0] + Mminus1[9] * v[1] + Mminus1[10] * v[2] + Mminus1[11] * v[3],
    Mminus1[12] * v[0] + Mminus1[13] * v[1] + Mminus1[14] * v[2] + Mminus1[15] * v[3]
};
NSLog(@"W : x = %f, y = %f, z = %f", w[0], w[1], w[2]);
worldPoint[0] = w[0];
worldPoint[1] = w[1];
worldPoint[2] = w[2];

}

datafatmunger
Actually, I don't understand why in the view -> world transformation you use negative entries (e.g. -m[8]). There shouldn't be any negative entries.Also, tPrime is wrong in my opinion. mv[12] should be mv[3], mv[13] should be mv[7] and mv[14] should be mv[11]. And mv is not declared anywhere (I assume it's the modelview).
Marc
Agreed, I've posted a better solution.
datafatmunger
A: 

Okay, okay that was still a bit buggy. Here is what is MOSTLY working now:

-(void)view2WorldPoint:(CGPoint)point :(GLfloat*)worldPoint {
float clickX = point.x;
float clickY = point.y;
float clickZ = -near;

//viewport -> normalized device coord -> clip
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);

GLfloat n[] = {
    (clickX - (float)viewport[0]) / (float)viewport[2] * 2.0 - 1.0,
    -((clickY - (float)viewport[1]) / (float)viewport[3] * 2.0 - 1.0),
    2.0 * clickZ - 1.0,
    1.0
};

GLfloat MP[16], MPInv[16];
MatMatMultiply(MP, projMat, modelMat);
GenerateInverseMatrix4f(MPInv, MP); // replace this one with the whole 1/p thang?

GLfloat w[] = {
    (MPInv[0]  * n[0]) + (MPInv[4]  * n[1]) + (MPInv[8]  * n[2]) + (MPInv[12] * n[3]),
    (MPInv[1]  * n[0]) + (MPInv[5]  * n[1]) + (MPInv[9]  * n[2]) + (MPInv[13] * n[3]),
    (MPInv[2]  * n[0]) + (MPInv[6]  * n[1]) + (MPInv[10] * n[2]) + (MPInv[14] * n[3]),
    (MPInv[3]  * n[0]) + (MPInv[7]  * n[1]) + (MPInv[11] * n[2]) + (MPInv[15] * n[3])
};

worldPoint[0] = w[0] / w[3];
worldPoint[1] = w[1] / w[3];
worldPoint[2] = w[2] / w[3];

}

float Determinant4f(const float m[16]) { return m[12]*m[9]*m[6]*m[3]- m[8]*m[13]*m[6]*m[3]- m[12]*m[5]*m[10]*m[3]+ m[4]*m[13]*m[10]*m[3]+ m[8]*m[5]*m[14]*m[3]- m[4]*m[9]*m[14]*m[3]- m[12]*m[9]*m[2]*m[7]+ m[8]*m[13]*m[2]*m[7]+ m[12]*m[1]*m[10]*m[7]- m[0]*m[13]*m[10]*m[7]- m[8]*m[1]*m[14]*m[7]+ m[0]*m[9]*m[14]*m[7]+ m[12]*m[5]*m[2]*m[11]- m[4]*m[13]*m[2]*m[11]- m[12]*m[1]*m[6]*m[11]+ m[0]*m[13]*m[6]*m[11]+ m[4]*m[1]*m[14]*m[11]- m[0]*m[5]*m[14]*m[11]- m[8]*m[5]*m[2]*m[15]+ m[4]*m[9]*m[2]*m[15]+ m[8]*m[1]*m[6]*m[15]- m[0]*m[9]*m[6]*m[15]- m[4]*m[1]*m[10]*m[15]+ m[0]*m[5]*m[10]*m[15]; }

BOOL GenerateInverseMatrix4f(float i[16], const float m[16]) { float x=Determinant4f(m); if (x==0) return FALSE;

i[0]= (-m[13]*m[10]*m[7] +m[9]*m[14]*m[7] +m[13]*m[6]*m[11]
       -m[5]*m[14]*m[11] -m[9]*m[6]*m[15] +m[5]*m[10]*m[15])/x;
i[4]= ( m[12]*m[10]*m[7] -m[8]*m[14]*m[7] -m[12]*m[6]*m[11]
       +m[4]*m[14]*m[11] +m[8]*m[6]*m[15] -m[4]*m[10]*m[15])/x;
i[8]= (-m[12]*m[9]* m[7] +m[8]*m[13]*m[7] +m[12]*m[5]*m[11]
       -m[4]*m[13]*m[11] -m[8]*m[5]*m[15] +m[4]*m[9]* m[15])/x;
i[12]=( m[12]*m[9]* m[6] -m[8]*m[13]*m[6] -m[12]*m[5]*m[10]
       +m[4]*m[13]*m[10] +m[8]*m[5]*m[14] -m[4]*m[9]* m[14])/x;
i[1]= ( m[13]*m[10]*m[3] -m[9]*m[14]*m[3] -m[13]*m[2]*m[11]
       +m[1]*m[14]*m[11] +m[9]*m[2]*m[15] -m[1]*m[10]*m[15])/x;
i[5]= (-m[12]*m[10]*m[3] +m[8]*m[14]*m[3] +m[12]*m[2]*m[11]
       -m[0]*m[14]*m[11] -m[8]*m[2]*m[15] +m[0]*m[10]*m[15])/x;
i[9]= ( m[12]*m[9]* m[3] -m[8]*m[13]*m[3] -m[12]*m[1]*m[11]
       +m[0]*m[13]*m[11] +m[8]*m[1]*m[15] -m[0]*m[9]* m[15])/x;
i[13]=(-m[12]*m[9]* m[2] +m[8]*m[13]*m[2] +m[12]*m[1]*m[10]
       -m[0]*m[13]*m[10] -m[8]*m[1]*m[14] +m[0]*m[9]* m[14])/x;
i[2]= (-m[13]*m[6]* m[3] +m[5]*m[14]*m[3] +m[13]*m[2]*m[7]
       -m[1]*m[14]*m[7] -m[5]*m[2]*m[15] +m[1]*m[6]* m[15])/x;
i[6]= ( m[12]*m[6]* m[3] -m[4]*m[14]*m[3] -m[12]*m[2]*m[7]
       +m[0]*m[14]*m[7] +m[4]*m[2]*m[15] -m[0]*m[6]* m[15])/x;
i[10]=(-m[12]*m[5]* m[3] +m[4]*m[13]*m[3] +m[12]*m[1]*m[7]
       -m[0]*m[13]*m[7] -m[4]*m[1]*m[15] +m[0]*m[5]* m[15])/x;
i[14]=( m[12]*m[5]* m[2] -m[4]*m[13]*m[2] -m[12]*m[1]*m[6]
       +m[0]*m[13]*m[6] +m[4]*m[1]*m[14] -m[0]*m[5]* m[14])/x;
i[3]= ( m[9]* m[6]* m[3] -m[5]*m[10]*m[3] -m[9]* m[2]*m[7]
       +m[1]*m[10]*m[7] +m[5]*m[2]*m[11] -m[1]*m[6]* m[11])/x;
i[7]= (-m[8]* m[6]* m[3] +m[4]*m[10]*m[3] +m[8]* m[2]*m[7]
       -m[0]*m[10]*m[7] -m[4]*m[2]*m[11] +m[0]*m[6]* m[11])/x;
i[11]=( m[8]* m[5]* m[3] -m[4]*m[9]* m[3] -m[8]* m[1]*m[7]
       +m[0]*m[9]* m[7] +m[4]*m[1]*m[11] -m[0]*m[5]* m[11])/x;
i[15]=(-m[8]* m[5]* m[2] +m[4]*m[9]* m[2] +m[8]* m[1]*m[6]
       -m[0]*m[9]* m[6] -m[4]*m[1]*m[10] +m[0]*m[5]* m[10])/x;

return TRUE;

}

void MatMatMultiply(GLfloat *result, GLfloat *matrix1, GLfloat *matrix2) { result[0]=matrix1[0]*matrix2[0]+ matrix1[4]*matrix2[1]+ matrix1[8]*matrix2[2]+ matrix1[12]*matrix2[3]; result[4]=matrix1[0]*matrix2[4]+ matrix1[4]*matrix2[5]+ matrix1[8]*matrix2[6]+ matrix1[12]*matrix2[7]; result[8]=matrix1[0]*matrix2[8]+ matrix1[4]*matrix2[9]+ matrix1[8]*matrix2[10]+ matrix1[12]*matrix2[11]; result[12]=matrix1[0]*matrix2[12]+ matrix1[4]*matrix2[13]+ matrix1[8]*matrix2[14]+ matrix1[12]*matrix2[15]; result[1]=matrix1[1]*matrix2[0]+ matrix1[5]*matrix2[1]+ matrix1[9]*matrix2[2]+ matrix1[13]*matrix2[3]; result[5]=matrix1[1]*matrix2[4]+ matrix1[5]*matrix2[5]+ matrix1[9]*matrix2[6]+ matrix1[13]*matrix2[7]; result[9]=matrix1[1]*matrix2[8]+ matrix1[5]*matrix2[9]+ matrix1[9]*matrix2[10]+ matrix1[13]*matrix2[11]; result[13]=matrix1[1]*matrix2[12]+ matrix1[5]*matrix2[13]+ matrix1[9]*matrix2[14]+ matrix1[13]*matrix2[15]; result[2]=matrix1[2]*matrix2[0]+ matrix1[6]*matrix2[1]+ matrix1[10]*matrix2[2]+ matrix1[14]*matrix2[3]; result[6]=matrix1[2]*matrix2[4]+ matrix1[6]*matrix2[5]+ matrix1[10]*matrix2[6]+ matrix1[14]*matrix2[7]; result[10]=matrix1[2]*matrix2[8]+ matrix1[6]*matrix2[9]+ matrix1[10]*matrix2[10]+ matrix1[14]*matrix2[11]; result[14]=matrix1[2]*matrix2[12]+ matrix1[6]*matrix2[13]+ matrix1[10]*matrix2[14]+ matrix1[14]*matrix2[15]; result[3]=matrix1[3]*matrix2[0]+ matrix1[7]*matrix2[1]+ matrix1[11]*matrix2[2]+ matrix1[15]*matrix2[3]; result[7]=matrix1[3]*matrix2[4]+ matrix1[7]*matrix2[5]+ matrix1[11]*matrix2[6]+ matrix1[15]*matrix2[7]; result[11]=matrix1[3]*matrix2[8]+ matrix1[7]*matrix2[9]+ matrix1[11]*matrix2[10]+ matrix1[15]*matrix2[11]; result[15]=matrix1[3]*matrix2[12]+ matrix1[7]*matrix2[13]+ matrix1[11]*matrix2[14]+ matrix1[15]*matrix2[15]; }

datafatmunger