PSPGL Viewport/Projection Oddities

Discuss the development of new homebrew software, tools and libraries.

Moderators: cheriff, TyRaNiD

Post Reply
bengarney
Posts: 24
Joined: Sat Oct 22, 2005 6:48 pm

PSPGL Viewport/Projection Oddities

Post by bengarney »

Howdy again!

I've got the following setup code that I use to manage my working area for rendering purposes. It works great on a varety of GL implementations (mac, pc, linux, on all drivers, and has for years...).

screenHeight and screenWidth are 479 and 271 respectively - one pixel less than my actual framebuffer size (just to be safe).

Code: Select all

   glMatrixMode(GL_PROJECTION); glLoadIdentity();

   glOrtho(rect.startX, rect.startX + rect.width,
           rect.height, 0,
           0, 1);
   glTranslatef(0, -rect.startY, 0);

   glMatrixMode(GL_MODELVIEW); glLoadIdentity();

   glViewport(rect.startX, screenHeight - (rect.startY + rect.height), rect.width, rect.height);
This code should implement the behavior that rendering is limited to the specified rectangle, with an orthographic projection on the XY plane (so that if I draw a 0,0, it's top left, and if I draw at 480, 272 it's bottom right, but things I try to draw outside the passed rect just get clipped out). However, it seems to be causing weird scaling and mispositioning problems, even though it work fine on many other GL implementations. (That snippet has worked reliably on ATI, nVidia, and other chipsets on mac, windows, and linux for about seven years.)

Help! :)

Thanks in advance,
Ben
bengarney
Posts: 24
Joined: Sat Oct 22, 2005 6:48 pm

Post by bengarney »

So, after some more experimenting, I find that the clipping is done against the proper rectangle, but the rendering isn't offset appropriately - it stays relative to the topleft corner, regardless of what x/y I pass to glViewport.

This seems related to a problem described in this thread: http://forums.ps2dev.org/viewtopic.php?t=3221

Doing some experimenting but I'm not feeling too confident - not as familiar with the innards of GU as no doubt many of you are here. :)
bengarney
Posts: 24
Joined: Sat Oct 22, 2005 6:48 pm

Post by bengarney »

And a little more testing suggests it's probably two things. First, the area to which the graphics are clipped is being set properly but the graphics themselves aren't offset properly, and second, I'm not sure that the GL offset semantics (ie, the lowerleft corner) are being preserved properly - it seems the PSP operates from top right.

I just can't quite figure out how to diddle the values I get in at glViewport properly such that they turn into the right values to put into the registers. I'll be doing trial and error but that's always painful. :)
jsgf
Posts: 254
Joined: Tue Jul 12, 2005 11:02 am
Contact:

Post by jsgf »

OK, that sounds easy enough to fix. I'll take a look at it when I get a chance. I really need a bug system to keep track of all this...
bengarney
Posts: 24
Joined: Sat Oct 22, 2005 6:48 pm

Post by bengarney »

Thanks! :)
bengarney
Posts: 24
Joined: Sat Oct 22, 2005 6:48 pm

Post by bengarney »

Some additional notes:

I meant top left for PSP, bottom left for GL. Don't think anything uses top right.

Code: Select all

	/* Viewport / Screen Offset */
	sendCommandi&#40;CMD_OFFSETX, &#40;2048 - width/2 - x&#41; << 4&#41;;
	sendCommandi&#40;CMD_OFFSETY, &#40;2048 - height/2 - y&#41; << 4&#41;;

	/* Viewport Size &#40;X/Y, Width/Height&#41; */
	sendCommandf&#40;CMD_VIEWPORT_SX, &#40;float&#41; &#40;width/2&#41;&#41;;
	sendCommandf&#40;CMD_VIEWPORT_SY, &#40;float&#41; &#40;-height/2&#41;&#41;;

	/* Viewport Center &#40;X/Y&#41; */
	sendCommandf&#40;CMD_VIEWPORT_TX, &#40;float&#41; &#40;2048 + x&#41;&#41;;
	sendCommandf&#40;CMD_VIEWPORT_TY, &#40;float&#41; &#40;2048 + y&#41;&#41;;

	/* Drawing Rectangle */
	sendCommandi&#40;CMD_REGION1, &#40;y << 10&#41; | x&#41;;
	sendCommandi&#40;CMD_REGION2, &#40;&#40;&#40;y + height&#41;-1&#41; << 10&#41; | &#40;&#40;x + width&#41;-1&#41;&#41;;
Notice the additional x/y terms. I suspect the solution involves something along these lines, but I'm not confident that my math is actually right in anything more than spirit. I know it's not, in fact, as it doesn't work right. :P
bengarney
Posts: 24
Joined: Sat Oct 22, 2005 6:48 pm

Post by bengarney »

Alrighty - here's a modified copy of the envmap sample app. You can move the viewport around by using the d-pad and the buttons on the right. One side controls X size/position and the other Y size/position.

Useful for visualizing what the values mean, and now that I've seen it, I expect I can get the PSPGL implementation to do what I mean. :)

Code: Select all

/*
 * PSP Software Development Kit - http&#58;//www.pspdev.org
 * -----------------------------------------------------------------------
 * Licensed under the BSD license, see LICENSE in PSPSDK root for details.
 *
 * envmap.c - Sample to desmonstrate environment mapping functionality
 *
 * Copyright &#40;c&#41; 2005 Jesper Svennevid
 * Copyright &#40;c&#41; 2005 Renaldas Zioma <rej@scene.lt>
 *
 */

#include <pspkernel.h>
#include <pspdisplay.h>
#include <pspdebug.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <pspctrl.h>

#include <pspgu.h>
#include <pspgum.h>

PSP_MODULE_INFO&#40;"EnvMap Sample", 0, 1, 1&#41;;
PSP_MAIN_THREAD_ATTR&#40;THREAD_ATTR_USER&#41;;

#define printf	pspDebugScreenPrintf

static unsigned int __attribute__&#40;&#40;aligned&#40;16&#41;&#41;&#41; list&#91;262144&#93;;
extern unsigned char env0_start&#91;&#93;;

typedef struct Vertex
&#123;
	float nx,ny,nz;
 	float x,y,z;
&#125; Vertex;

int SetupCallbacks&#40;&#41;;

void genTorus&#40; unsigned slices, unsigned rows, float radius, float thickness,
	Vertex* dstVertices, unsigned short* dstIndices &#41;;

#define BUF_WIDTH &#40;512&#41;
#define SCR_WIDTH &#40;480&#41;
#define SCR_HEIGHT &#40;272&#41;
#define PIXEL_SIZE &#40;4&#41; /* change this if you change to another screenmode */
#define FRAME_SIZE &#40;BUF_WIDTH * SCR_HEIGHT * PIXEL_SIZE&#41;
#define ZBUF_SIZE &#40;BUF_WIDTH SCR_HEIGHT * 2&#41; /* zbuffer seems to be 16-bit? */

#define TORUS_SLICES 48 // numc
#define TORUS_ROWS 48 // numt
#define TORUS_RADIUS 1.0f
#define TORUS_THICKNESS 0.5f

#define LIGHT_DISTANCE 3.0f

Vertex __attribute__&#40;&#40;aligned&#40;16&#41;&#41;&#41; torus_vertices&#91;TORUS_SLICES*TORUS_ROWS&#93;;
unsigned short __attribute__&#40;&#40;aligned&#40;16&#41;&#41;&#41; torus_indices&#91;TORUS_SLICES*TORUS_ROWS*6&#93;;

int main&#40;int argc, char* argv&#91;&#93;&#41;
&#123;
	pspDebugScreenInit&#40;&#41;;
	SetupCallbacks&#40;&#41;;

	// generate geometry
	genTorus&#40; TORUS_ROWS, TORUS_SLICES, TORUS_RADIUS, TORUS_THICKNESS, torus_vertices, torus_indices &#41;;

	// flush cache so that no stray data remains

	sceKernelDcacheWritebackAll&#40;&#41;;

    //setup Pad

    SceCtrlData pad;
    u32 oldButtons = 0;
    sceCtrlSetSamplingCycle&#40;0&#41;;
    sceCtrlSetSamplingMode&#40;0&#41;;

	// setup GU

	sceGuInit&#40;&#41;;

	sceGuStart&#40;GU_DIRECT,list&#41;;
	sceGuDrawBuffer&#40;GU_PSM_8888,&#40;void*&#41;0,BUF_WIDTH&#41;;
	sceGuDispBuffer&#40;SCR_WIDTH,SCR_HEIGHT,&#40;void*&#41;0x88000,BUF_WIDTH&#41;;
	sceGuDepthBuffer&#40;&#40;void*&#41;0x110000,BUF_WIDTH&#41;;
	sceGuOffset&#40;2048 - &#40;SCR_WIDTH/2&#41;,2048 - &#40;SCR_HEIGHT/2&#41;&#41;;
	sceGuViewport&#40;2048,2048,SCR_WIDTH,SCR_HEIGHT&#41;;
	sceGuDepthRange&#40;0xc350,0x2710&#41;;
	sceGuScissor&#40;0,0,SCR_WIDTH,SCR_HEIGHT&#41;;
	sceGuEnable&#40;GU_SCISSOR_TEST&#41;;
	sceGuDepthFunc&#40;GU_GEQUAL&#41;;
	sceGuEnable&#40;GU_DEPTH_TEST&#41;;
	sceGuFrontFace&#40;GU_CW&#41;;
	sceGuShadeModel&#40;GU_SMOOTH&#41;;
	sceGuEnable&#40;GU_CULL_FACE&#41;;
	sceGuEnable&#40;GU_CLIP_PLANES&#41;;
	sceGuEnable&#40;GU_TEXTURE_2D&#41;;
	sceGuEnable&#40;GU_LIGHTING&#41;;
	sceGuEnable&#40;GU_LIGHT0&#41;;
	sceGuFinish&#40;&#41;;
	sceGuSync&#40;0,0&#41;;
	sceDisplayWaitVblankStart&#40;&#41;;
	sceGuDisplay&#40;GU_TRUE&#41;;

	// run sample

	int valTorus = 0;
	int valEnvMap = 0;
	int rotateTorus = 1;
	int rotateEnvMap = 1;

    int startX = 2048;
    int startY = 2048;
    int sizeX = 480;
    int sizeY = 272;

	for&#40;;;&#41;
	&#123;
		sceGuStart&#40;GU_DIRECT,list&#41;;

		// Diddle the viewport
		sceGuViewport&#40;startX, startY, sizeX, sizeY&#41;;

		// clear screen

		sceGuClearColor&#40;0x554433&#41;;
		sceGuClearDepth&#40;0&#41;;
		sceGuClear&#40;GU_COLOR_BUFFER_BIT|GU_DEPTH_BUFFER_BIT&#41;;

		// setup a light

		ScePspFVector3 dir = &#123; 0, 0, 1 &#125;;
		sceGuLight&#40;0,GU_DIRECTIONAL,GU_DIFFUSE,&dir&#41;;
		sceGuLightColor&#40;0,GU_DIFFUSE,0x00ff4040 &#41;;
		sceGuLightAtt&#40;0,1.0f,0.0f,0.0f&#41;;
		sceGuAmbient&#40;0x00202020&#41;;

		// setup proj/view matrices

		sceGumMatrixMode&#40;GU_PROJECTION&#41;;
		sceGumLoadIdentity&#40;&#41;;
		sceGumPerspective&#40;75.0f,16.0/9.0f,1.0f,1000.0f&#41;;

		sceGumMatrixMode&#40;GU_VIEW&#41;;
		sceGumLoadIdentity&#40;&#41;;

		// setup envmap texture

		sceGuTexMode&#40;GU_PSM_4444,0,0,0&#41;;
		sceGuTexImage&#40;0,64,64,64,env0_start&#41;;
		sceGuTexFunc&#40;GU_TFX_MODULATE,GU_TCC_RGB&#41;;
		sceGuTexFilter&#40;GU_LINEAR,GU_LINEAR&#41;;
		sceGuAmbientColor&#40;0xffffffff&#41;;

		// setup envmap matrix &#40;2x3&#41;
		// 2d rotation/translation matrix
		// &#91; cosa  -sina &#93;
		// &#91; sina   cosa &#93;
		// &#91;  tx     ty  &#93;

		// envmap matrix is transposed
		// since it is passed to GU as columns
		float angle = -2.0f * valEnvMap * &#40;GU_PI/180.0f&#41;;
		float cs = cosf&#40;angle&#41;;
		float sn = sinf&#40;angle&#41;;
		ScePspFVector3 envmapMatrixColumns&#91;2&#93; = &#123;
			&#123; cs,  sn, 0.0f &#125;,
			&#123; -sn, cs, 0.0f &#125;
		&#125;;
		sceGuLight&#40; 2, GU_DIRECTIONAL, GU_DIFFUSE, &envmapMatrixColumns&#91;0&#93; &#41;;
		sceGuLight&#40; 3, GU_DIRECTIONAL, GU_DIFFUSE, &envmapMatrixColumns&#91;1&#93; &#41;;

		// setup envmap texture coord generation
		sceGuTexMapMode&#40;
			GU_ENVIRONMENT_MAP,	// envmap mode on
			2,	// use 2nd light position as an envmap matrix 1st column
			3	// use 3rd light position as an envmap matrix 2nd column
			&#41;;

		// draw torus

		sceGumMatrixMode&#40;GU_MODEL&#41;;
		&#123;
			ScePspFVector3 pos = &#123;0,0,-2.5f&#125;;
			ScePspFVector3 rot = &#123;valTorus * 0.79f * &#40;GU_PI/180.0f&#41;, valTorus * 0.98f * &#40;GU_PI/180.0f&#41;, valTorus * 1.32f * &#40;GU_PI/180.0f&#41;&#125;;
			sceGumLoadIdentity&#40;&#41;;
			sceGumTranslate&#40;&pos&#41;;
			sceGumRotateXYZ&#40;&rot&#41;;
		&#125;

		sceGuColor&#40;0xffffff&#41;;
		sceGumDrawArray&#40;GU_TRIANGLES,GU_NORMAL_32BITF|GU_VERTEX_32BITF|GU_INDEX_16BIT|GU_TRANSFORM_3D,sizeof&#40;torus_indices&#41;/sizeof&#40;unsigned short&#41;,torus_indices,torus_vertices&#41;;

		pspDebugScreenSetXY&#40;0,0&#41;;
		printf&#40; "Press X to toggle environment map rotation" &#41;;
		pspDebugScreenSetXY&#40;0,1&#41;;
		printf&#40; "Press O to toggle torus rotation" &#41;;
		pspDebugScreenSetXY&#40;0,2&#41;;
		printf&#40; " rect = %d, %d, %d, %d", startX, startY, sizeX, sizeY &#41;;

		sceCtrlReadBufferPositive&#40;&pad, 1&#41;;
		if&#40;oldButtons != pad.Buttons&#41;
		&#123;
			if&#40;pad.Buttons & PSP_CTRL_CROSS&#41;
				rotateTorus ^= 0x01;
			if&#40;pad.Buttons & PSP_CTRL_CIRCLE&#41;
				rotateEnvMap ^= 0x01;
		&#125;
		oldButtons = pad.Buttons;

			// Increase startX
			if&#40;pad.Buttons & PSP_CTRL_UP&#41;
				startX++;

			// Decrease startX
			if&#40;pad.Buttons & PSP_CTRL_DOWN&#41;
				startX--;

			// Increase sizeX
			if&#40;pad.Buttons & PSP_CTRL_RIGHT&#41;
				sizeX++;

			// Decreate sizeY
			if&#40;pad.Buttons & PSP_CTRL_LEFT&#41;
				sizeX--;


			// Increase startX
			if&#40;pad.Buttons & PSP_CTRL_TRIANGLE&#41;
				startY++;

			// Decrease startX
			if&#40;pad.Buttons & PSP_CTRL_CROSS&#41;
				startY--;

			// Increase sizeX
			if&#40;pad.Buttons & PSP_CTRL_CIRCLE&#41;
				sizeY++;

			// Decreate sizeY
			if&#40;pad.Buttons & PSP_CTRL_SQUARE&#41;
				sizeY--;

		sceGuFinish&#40;&#41;;
		sceGuSync&#40;0,0&#41;;

//		sceDisplayWaitVblankStart&#40;&#41;;
		sceGuSwapBuffers&#40;&#41;;

		if&#40;rotateTorus&#41; valTorus++;
		if&#40;rotateEnvMap&#41; valEnvMap++;
	&#125;

	sceGuTerm&#40;&#41;;

	sceKernelExitGame&#40;&#41;;
	return 0;
&#125;

/* Exit callback */
int exit_callback&#40;int arg1, int arg2, void *common&#41;
&#123;
	sceKernelExitGame&#40;&#41;;
	return 0;
&#125;

/* Callback thread */
int CallbackThread&#40;SceSize args, void *argp&#41;
&#123;
	int cbid;

	cbid = sceKernelCreateCallback&#40;"Exit Callback", exit_callback, NULL&#41;;
	sceKernelRegisterExitCallback&#40;cbid&#41;;

	sceKernelSleepThreadCB&#40;&#41;;

	return 0;
&#125;

/* Sets up the callback thread and returns its thread id */
int SetupCallbacks&#40;void&#41;
&#123;
	int thid = 0;

	thid = sceKernelCreateThread&#40;"update_thread", CallbackThread, 0x11, 0xFA0, 0, 0&#41;;
	if&#40;thid >= 0&#41;
	&#123;
		sceKernelStartThread&#40;thid, 0, 0&#41;;
	&#125;

	return thid;
&#125;

/* usefull geometry functions */
void genTorus&#40; unsigned slices, unsigned rows, float radius, float thickness, Vertex* dstVertices, unsigned short* dstIndices &#41;
&#123;
	unsigned int i,j;

	// generate torus &#40;TODO&#58; tri-strips&#41;
	for &#40;j = 0; j < slices; ++j&#41;
	&#123;
		for &#40;i = 0; i < rows; ++i&#41;
		&#123;
			struct Vertex* curr = &dstVertices&#91;i+j*rows&#93;;
			float s = i + 0.5f;
			float t = j;
			float cs,ct,ss,st;

			cs = cosf&#40;s * &#40;2*GU_PI&#41;/slices&#41;;
			ct = cosf&#40;t * &#40;2*GU_PI&#41;/rows&#41;;
			ss = sinf&#40;s * &#40;2*GU_PI&#41;/slices&#41;;
			st = sinf&#40;t * &#40;2*GU_PI&#41;/rows&#41;;

			curr->nx = cs * ct;
			curr->ny = cs * st;
			curr->nz = ss;

			curr->x = &#40;radius + thickness * cs&#41; * ct;
			curr->y = &#40;radius + thickness * cs&#41; * st;
			curr->z = thickness * ss;
		&#125;
	&#125;

	for &#40;j = 0; j < slices; ++j&#41;
	&#123;
		for &#40;i = 0; i < rows; ++i&#41;
		&#123;
			unsigned short* curr = &dstIndices&#91;&#40;i+&#40;j*rows&#41;&#41;*6&#93;;
			unsigned int i1 = &#40;i+1&#41;%rows, j1 = &#40;j+1&#41;%slices;

			*curr++ = i + j * rows;
			*curr++ = i1 + j * rows;
			*curr++ = i + j1 * rows;

			*curr++ = i1 + j * rows;
			*curr++ = i1 + j1 * rows;
			*curr++ = i + j1 * rows;
		&#125;
	&#125;
&#125;
bengarney
Posts: 24
Joined: Sat Oct 22, 2005 6:48 pm

And a solution...

Post by bengarney »

And the new glViewport.c, that seems to be working - but takes a topleft x,y instead of a bottomleft, so beware!

Code: Select all

#include "pspgl_internal.h"

void glViewport &#40;GLint x, GLint y, GLsizei width, GLsizei height&#41;
&#123;
	pspgl_curctx->viewport.x = x;
	pspgl_curctx->viewport.y = y;
	pspgl_curctx->viewport.width = width;
	pspgl_curctx->viewport.height = height;

	// Figure appropriate center values
	int cx = 2048 - &#40;pspgl_curctx->draw->width / 2&#41; + &#40;width / 2&#41; + x;
	int cy = 2048 - &#40;pspgl_curctx->draw->height / 2&#41; + &#40;height / 2&#41; + y;

	/* Viewport / Screen Offset */
	sendCommandi&#40;CMD_OFFSETX, &#40;&#40;2048 - &#40;480/2&#41;&#41;&#41; << 4&#41;;
	sendCommandi&#40;CMD_OFFSETY, &#40;&#40;2048 - &#40;272/2&#41;&#41;&#41; << 4&#41;;

	/* Viewport Size &#40;X/Y, Width/Height&#41; */
	sendCommandf&#40;66,&#40;float&#41;&#40;width>>1&#41;&#41;;
	sendCommandf&#40;67,&#40;float&#41;&#40;&#40;&#40;-height&#41; + &#40;&#40;-height&#41; >> 31&#41;&#41;>>1&#41;&#41;;

	/* Viewport Center &#40;X/Y&#41; */
	sendCommandf&#40;69, &#40;float&#41; &#40;cx&#41;&#41;;
	sendCommandf&#40;70, &#40;float&#41; &#40;cy&#41;&#41;;

	/* Drawing Rectangle */
	y = 0; x = 0;
	height = 272; width = 480;
	sendCommandi&#40;CMD_REGION1, &#40;y << 10&#41; | x&#41;;
	sendCommandi&#40;CMD_REGION2, &#40;&#40;&#40;y+height&#41;-1&#41; << 10&#41; | &#40;&#40;x+width&#41;-1&#41;&#41;;

    return;
&#125;
Don't need the error checking, as it'll draw fine with out of range values (as long as you stay within the 4k range, I assume...).

The Drawing Rect update is probably not needed either, but I do it anyway.

So, working mostly! :)

Luckily, I only call glViewport from two places so all I have to do is make it so that those two places don't correct for GL's bottom left convention.

Hope this can make it into the official PSPGL repo someday soon. :)
jsgf
Posts: 254
Joined: Tue Jul 12, 2005 11:02 am
Contact:

Post by jsgf »

I just checked in a correct version of glViewport, which uses y=0 is bottom. I also fixed glScissor in the same way. I also added a "viewports" demo/test to exercise it.
bengarney
Posts: 24
Joined: Sat Oct 22, 2005 6:48 pm

Post by bengarney »

Thanks!
Post Reply