I've been working on a program where I want to know where something will appear in screen coordinates to decide which camera angle is the best. So, after peeking at the Mesa source code, I've come up with the following:
Code: Select all
/** Project a point from object coordinates to screen coordinates.
* \param object point in object coordinates.
* \param view view transformation matrix.
* \param projection projection transformation matrix (for example from gumPerspective).
* \param model model transformation matrix (for example from gumLookAt).
* \param viewport the left, top, width and height of the viewport.
* \param screen on success, the result of the point projected into screen coordinates.
* \returns 1 on success, or 0 if it is a degenerate projection.
*/
s32 gumProject(const ScePspFVector3 *object,const ScePspFMatrix4 *view,const ScePspFMatrix4 *projection,const ScePspFMatrix4 *model,const s32 viewport[4],ScePspFVector3 *screen) {
ScePspFMatrix4 m;
float w;
gumMultMatrix(&m,view,projection);
gumMultMatrix(&m,&m,model);
screen->x=object->x*m.x.x+object->y*m.y.x+object->z*m.z.x+m.w.x;
screen->y=object->x*m.x.y+object->y*m.y.y+object->z*m.z.y+m.w.y;
screen->z=object->x*m.x.z+object->y*m.y.z+object->z*m.z.z+m.w.z;
w=object->x*m.x.w+object->y*m.y.w+object->z*m.z.w+m.w.w;
if(w==0) return FALSE;
screen->x/=w;
screen->y/=w;
screen->z/=w;
// Now map x,y to the range 0-1
screen->x=screen->x*0.5f+0.5f;
screen->y=screen->y*0.5f+0.5f;
// Scale/translate to the viewport size
screen->x=screen->x*viewport[2]+viewport[0]; // *width + left
screen->y=(1.0f-screen->y)*viewport[3]+viewport[1]; // *height+top
return TRUE;
}
ScePspFMatrix4 view,projection,mode;
ScePspFVector3 from={-20,-10,-20},to={0,0,0},up={0,0,1}; // For example
ScePspFVector3 obj={2,1,0},screen;
s32 viewport[4]={0,0,480,272};
gumLoadIdentity(&view);
gumLoadIdentity(&projection);
gumPerspective(&projection,45.0f,16.0f/9.0f,1.5f,2048.0f);
gumLoadIdentity(&model);
gumLookAt(&model,&from,&to,&up);
// Now project a point obj -> screen
gumProject(&obj,&view,&projection,&model,viewport,&screen);
which should correspond to a point drawn with:
sceGumMatrixMode(GU_VIEW);
sceGumLoadIdentity();
sceGumMatrixMode(GU_PROJECTION);
sceGumLoadIdentity();
sceGumPerspective(45.0f,16.0f/9,0f,1.5f,2048.0f);
sceGumMatrixMode(GU_MODEL);
sceGumLoadIdentity();
sceGumLookAt(&from,&to,&up);
From testing it, I find that all of my projected values seem to be in the range:
x in 238 to 242 for on screen values. I just don't get it. Any ideas?
Edit: updated sample to fix cut and paste errors. Now works perfectly. :-) Enjoy.