This is something very similar to an isometric interface for an iPad project I'm working on. That is the same screen resolution as your project. I was building other project code a couple days ago, when I first read your question. But the interface code for picking objects needed to be built. So it made sense to try developing the code. I setup a test using a blank coloured checkerboard to fit your question.
Here is a quick video demonstrating it. Please pardon the ugly look of it. Also please note about the video, the values following the cursor are are integers, but the code produces float values. There was another function I created to do that extra work for my own use. If that's something you'd like, I'll include it in my answer.
http://youtu.be/JyddfSf57ic

This code is deliberately verbose, because I am rusty at the math. So I have spent many hours the last couple days re-learning the dot and cross products, also reading barycentric code but deciding against it.
Last thing before the code: there is an issue with your projection matrix in the question. You are including the camera transform in the projection matrix. Technically that is allowed, but there are many resources online that suggest it's bad practice.
Hope this helps!
void calculateTouchPointOnGrid(CGPoint screenTouch) {
float backingWidth = 1024;
float backingHeight = 768;
float aspect = backingWidth / backingHeight;
float zNear = 0;
float zFar = 20;
glm::detail::tvec3<float> unprojectedNearZ;
glm::detail::tvec3<float> unprojectedFarZ;
/*
Window coordinates, including zNear
This code uses zNear and zFar as two arbitrary values of
magnitude along the direction (vector) of the camera's
view (affected by projection and model-view matrices) that enable
determining the line/ray, originating from screenTouch (or
mouse click, etc) that later intersects the plane.
*/
glm::vec3 win = glm::vec3( screenTouch.x, backingHeight - screenTouch.y, zNear);
// Model & View matrix
glm::detail::tmat4x4<float> modelTransformMatrix = glm::detail::tmat4x4<float>(1);
modelTransformMatrix = glm::translate(modelTransformMatrix, glm::detail::tvec3<float>(0,0,-1));
modelTransformMatrix = glm::rotate(modelTransformMatrix, 0.f, glm::detail::tvec3<float>(0,1,0));
modelTransformMatrix = glm::scale(modelTransformMatrix, glm::detail::tvec3<float>(1,1,1));
glm::detail::tmat4x4<float> modelViewMatrix = lookAtMat * modelTransformMatrix;
/*
Projection
*/
const glm::detail::tmat4x4<float> projectionMatrix =
glm::ortho<float>(-aspect*5, aspect*5, -5, 5, 0, 20);
/*
Viewport
*/
const glm::vec4 viewport = glm::vec4(0, 0, backingWidth, backingHeight);
/*
Calculate two points on a line/ray based on the window coordinate (including arbitrary Z value), plus modelViewMatrix, projection matrix and viewport
*/
unprojectedNearZ = glm::unProject(win,
modelViewMatrix,
projectionMatrix,
viewport);
win[2] = zFar;
unprojectedFarZ = glm::unProject(win,
modelViewMatrix,
projectionMatrix,
viewport);
/*
Define the start of the ray
*/
glm::vec3 rayStart( unprojectedNearZ[0], unprojectedNearZ[1], unprojectedNearZ[2] );
/*
Determine the vector traveling parallel to the camera from the two
unprojected points
*/
float lookatVectX = unprojectedFarZ[0] - unprojectedNearZ[0];
float lookatVectY = unprojectedFarZ[1] - unprojectedNearZ[1];
float lookatVectZ = unprojectedFarZ[2] - unprojectedNearZ[2];
glm::vec3 dir( lookatVectX, lookatVectY, lookatVectZ );
/*
Define three points on the plane that will define a triangle.
Winding order does not matter.
*/
glm::vec3 p0(0,0,0);
glm::vec3 p1(1,0,0);
glm::vec3 p2(0,0,1);
/*
And finally the destination of the calculations
*/
glm::vec3 linePlaneIntersect;
if (cartesianLineIntersectPlane(rayStart, dir, p0, p1, p2, linePlaneIntersect)) {
// do work here using the linePlaneIntersect values
}
}
bool cartesianLineIntersectPlane(glm::vec3 rayStart, glm::vec3 dir,
glm::vec3 p0, glm::vec3 p1, glm::vec3 p2, glm::vec3 &linePlaneIntersect) {
/*
Create edge vectors to form the plane normal
*/
glm::vec3 edge1 = p1 - p0;
glm::vec3 edge2 = p2 - p0;
/*
Check if the ray direction is parallel to plane, before continuing
*/
glm::vec3 perpendicularvector = glm::cross(dir, edge2);
/*
dot product of edge1 on perpendicular vector
if orthogonal (approximately 0) then ray is parallel to plane, has 0 or infinite contact points
*/
float det = glm::dot(edge1, perpendicularvector);
float Epsilon = std::numeric_limits<float>::epsilon();
if (det > -Epsilon && det < Epsilon)
// Parallel, return false
return false;
/*
Calculate the normalized/unit normal vector
*/
glm::vec3 planeNormal = glm::normalize( glm::cross(edge1, edge2) );
/*
Calculate d, the dot product of the normal and any point on the plane.
D is the magnitude of the plane's normal, to the origin.
*/
float d = planeNormal[0] * p0[0] + planeNormal[1] * p0[1] + planeNormal[2] * p0[2];
/*
Take the x,y,z equations, ie:
finalP.xyz = raystart.xyz + dir.xyz * t
substitute them into the scalar equation for plane, ie:
ax + by + cz = d
and solve for t. (This gets a bit wordy) t is the
multiplier on the direction vector, originating at raystart,
where it intersects the plane.
eg:
ax + by + cz = d
a(raystart.x + dir.x * t) + b(raystart.y + dir.y * t) + c(raystart.z + dir.z * t) = d
a(raystart.x) + a(dir.x*t) + b(raystart.y) + b(dir.y*t) + c(raystart.z) + c(dir.z*t) = d
a(raystart.x) + a(dir.x*t) + b(raystart.y) + b(dir.y*t) + c(raystart.z) + c(dir.z*t) - d = 0
a(raystart.x) + b(raystart.y) + c(raystart.z) - d = - a(dir.x*t) - b(dir.y*t) - c(dir.z*t)
(a(raystart.x) + b(raystart.y) + c(raystart.z) - d) / ( a(-dir.x) + b(-dir.y) + c(-dir.z) = t
*/
float leftsideScalars = (planeNormal[0] * rayStart[0] +
planeNormal[1] * rayStart[1] +
planeNormal[2] * rayStart[2] - d);
float directionDotProduct = (planeNormal[0] * (-dir[0]) +
planeNormal[1] * (-dir[1]) +
planeNormal[2] * (-dir[2]) );
/*
Final calculation of t, hurrah!
*/
float t = leftsideScalars / directionDotProduct;
/*
This is the particular value of t for that line that lies
in the plane. Then you can solve for x, y, and z by going
back up to the line equations and substituting t back in.
*/
linePlaneIntersect = glm::vec3(rayStart[0] + dir[0] * t,
rayStart[1] + dir[1] * t,
rayStart[2] + dir[2] * t
);
return true;
}