I have been working for the last few days to "unproject" our app's touch events into our Renderer's coordinate space. In the pursuit of this goal, I have tried various custom unproject methods and alternative techniques (including trying to convert the coordinates using the scaling factor & transform values) to no end. I have gotten close (where my touches are slightly off) however my attempts using GLU.gluUnProject
have been way off, usually placing the coordinates around the center of the view. The "closest" results were produced by Xandy's method however even these are usually off. My primary questions are how do I setup my viewport matrix and am I passing GLU.gluUnProject
correct parameters? My math is based on the answer to this question. Here are the relevant excerpts of my code (showing how I setup my matrices and my current attempt):
public void onSurfaceChanged(GL10 gl, int width, int height) {
// Set the OpenGL viewport to fill the entire surface.
glViewport(0, 0, width, height);
...
float ratio = (float) width / height;
Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
}
public void onDrawFrame(GL10 gl) {
glClear(GL_COLOR_BUFFER_BIT);
Matrix.setLookAtM(mViewMatrix, 0, 0f, 0f, -4.5f, 0f, 0f, 0f, 0f, 1f, 0f);
Matrix.scaleM(mViewMatrix, 0, mScaleFactor, mScaleFactor, 1.0f);
Matrix.translateM(mViewMatrix, 0, -mOffset.x, mOffset.y, 0.0f);
Matrix.multiplyMM(mModelMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
...
}
public PointF convertScreenCoords(float x, float y) {
int[] viewPortMatrix = new int[]{0, 0, (int)mViewportWidth, (int)mViewportHeight};
float[] outputNear = new float[4];
float[] outputFar = new float[4];
y = mViewportHeight - y;
int successNear = GLU.gluUnProject(x, y, 0, mModelMatrix, 0, mProjectionMatrix, 0, viewPortMatrix, 0, outputNear, 0);
int successFar = GLU.gluUnProject(x, y, 1, mModelMatrix, 0, mProjectionMatrix, 0, viewPortMatrix, 0, outputFar, 0);
if (successNear == GL_FALSE || successFar == GL_FALSE) {
throw new RuntimeException("Cannot invert matrices!");
}
convert4DCoords(outputNear);
convert4DCoords(outputFar);
float distance = outputNear[2] / (outputFar[2] - outputNear[2]);
float normalizedX = (outputNear[0] + (outputFar[0] - outputNear[0]) * distance);
float normalizedY = (outputNear[1] + (outputFar[1] - outputNear[1]) * distance);
return new PointF(normalizedX, normalizedY);
}
convert4DCoords
is simply a helper function that divides each coordinate (x, y, z) of an array by w. mOffset
and mScaleFactor
are the translation and scaling parameters (given by a ScaleGestureDetector
within our GLSurfaceView
)
Based on everything I have read this should be working however it is consistently wrong and I am not sure what else to try. Any help/feedback would be greatly appreciated!