FEATURED FEATURED Health LIFE TECH

Displaying three-dimensional graphics on PSP / SurprizingFacts

A couple of months ago, I again took a dusty PSP out of the box and decided to port it to my previously-shown engine. With software rendering problems did not arise – everything works. But with the use of GU everything was not so simple. In this article I will show by example how you can write a simple three-dimensional application for PSP using GU.

I warn you in advance that the programming manuals for PSP are quite small, and therefore some of my conclusions may be incorrect. But, to the point.

The main function of the program for PSP, if anyone does not know, is formatted like this:

  #include 
#include 
#include 

// ------------------------------------------------ ----------------------------------------
PSP_MODULE_INFO ("GUTexture", 0, 1, 1);
PSP_MAIN_THREAD_ATTR (THREAD_ATTR_USER | THREAD_ATTR_VFPU);

Void dump_threadstatus (void);

Bool done = false;

Int exit_callback (int arg1, int arg2, void * common)
{
 Done = true;
 Return (0);
}

Int CallbackThread (SceSize args, void * argp)
{
 Int cbid;
 Cbid = sceKernelCreateCallback ("Exit Callback", exit_callback, NULL);
 SceKernelRegisterExitCallback (cbid);
 SceKernelSleepThreadCB ();
 Return (0);
}
Int SetupCallbacks (void)
{
 Int thid = 0;
 Thid = sceKernelCreateThread ("update_thread", CallbackThread, 0x11,0xFA0,0,0);
 If (thid> = 0) sceKernelStartThread (thid, 0, 0);
 Return (thid);
}
// ------------------------------------------------ ----------------------------------------
// start the program
// ------------------------------------------------ ----------------------------------------

Int main (int argc, char ** argv)
{
 PspDebugScreenInit ();
 // set the handlers
 SetupCallbacks ();
 // execute the program
 ..........
 // exit the program
 SceKernelExitGame ();
 Return (0);
}
 

The GU initialization is performed as follows:

First we request pointers to three buffers – screen, offscreen and depth buffer (Z-buffer). The buffer is aligned by 512 bytes per line (although the PSP line has 480 pixels).

 ]  // the sizes of the screen
#define SCREEN_WIDTH 480
#define SCREEN_HEIGHT 272
#define SCREEN_LINE_WIDTH 512

Void * fbp0 = getStaticVramBuffer (SCREEN_LINE_WIDTH, SCREEN_HEIGHT, GU_PSM_8888);
Void * fbp1 = getStaticVramBuffer (SCREEN_LINE_WIDTH, SCREEN_HEIGHT, GU_PSM_8888);
Void * zbp = getStaticVramBuffer (SCREEN_LINE_WIDTH, SCREEN_HEIGHT, GU_PSM_4444);
 

The function of requesting pointer to buffers is defined as

  #include 
#include 

Static unsigned int staticOffset = 0;

Static unsigned int getMemorySize (unsigned int width, unsigned int height, unsigned int psm)
{
 Switch (psm)
 {
  Case GU_PSM_T4: return ((width * height) >> 1);
  Case GU_PSM_T8: return (width * height);
  Case GU_PSM_5650:
  Case GU_PSM_5551:
  Case GU_PSM_4444:
  Case GU_PSM_T16: return (2 * width * height);
  Case GU_PSM_8888:
  Case GU_PSM_T32: return (4 * width * height);
  Default: return (0);
 }
}

Void * getStaticVramBuffer (unsigned int width, unsigned int height, unsigned int psm)
{
 Unsigned int memSize = getMemorySize (width, height, psm);
 Void * result = (void *) staticOffset;
 StaticOffset + = memSize;
 Return (result);
}

Void * getStaticVramTexture (unsigned int width, unsigned int height, unsigned int psm)
{
 Void * result = getStaticVramBuffer (width, height, psm);
 Return ((void *) (((unsigned int) result) + ((unsigned int) sceGeEdramGetAddr ())));
}
 

These are not my functions – I took them from a program a long time ago and only slightly changed. Memory is allocated in the video memory area. Textures should also be placed where possible, otherwise the performance will drop dramatically.

The GU at PSP is similar to OpenGL. The execution of commands requires their placement in the display list, and the memory for this list should be pre-allocated and aligned:

  // the size of the sides of the virtual PSP screen
#define VIRTUAL_SCREEN_SIZE 2048
// aspect ratio of the screen
#define SCREEN_ASPECT 16.0f / 9.0f
// front clipping plane
#define NEAR_PLANE_Z 5.0f
// rear clipping plane
#define FAR_PLANE_Z 4096.0f
//vision angle
#define EYE_ANGLE 60.0f

// initialize the graph GU
 SceGuInit ();
 // create and run a new context for the display - it should be executed immediately, because GU_DIRECT
 SceGuStart (GU_DIRECT, DisplayList);
 // set the parameters of the drawing buffer-pixel format, pointer to video memory area, string length (aligned, not physical)
 SceGuDrawBuffer (GU_PSM_8888, fbp0, SCREEN_LINE_WIDTH);
 // set the parameters of the screen buffer - the size of the screen, the pointer to video memory, the length of the line
 SceGuDispBuffer (SCREEN_WIDTH, SCREEN_HEIGHT, fbp1, SCREEN_LINE_WIDTH);
 // set the buffer parameters of the depth-pointer to the beginning of the depth buffer in the video memory and the length of the string
 SceGuDepthBuffer (zbp, SCREEN_LINE_WIDTH);
 // set the screen offset in the total space of 4096x4096 (in PSP this is the size of the virtual screen)
 SceGuOffset (VIRTUAL_SCREEN_SIZE- (SCREEN_WIDTH / 2), VIRTUAL_SCREEN_SIZE- (SCREEN_HEIGHT / 2)); // center
 // configure the viewport - the viewport - the coordinates of the center and the dimensions of the sides
 SceGuViewport (VIRTUAL_SCREEN_SIZE, VIRTUAL_SCREEN_SIZE, SCREEN_WIDTH, SCREEN_HEIGHT);
 // set the range of values ​​for the depth buffer - the front and back clipping planes (the buffer is inverted and values ​​from 0 to 65535!)
 SceGuDepthRange (65535.0);
 // enable cropping of the display area by the viewport size
 SceGuScissor (0.0, SCREEN_WIDTH, SCREEN_HEIGHT);
 SceGuEnable (GU_SCISSOR_TEST);
 SceGuEnable (GU_CLIP_PLANES);
  // set up the projection matrix
 SceGumMatrixMode (GU_PROJECTION);
 SceGumLoadIdentity ();
 SceGumPerspective (EYE_ANGLE, SCREEN_ASPECT, NEAR_PLANE_Z, FAR_PLANE_Z);
 // turn on the mode of smooth interpolation of the color of the faces
 SceGuShadeModel (GU_SMOOTH);
 // enable the depth test
 SceGuDepthFunc (GU_GEQUAL);
 SceGuEnable (GU_DEPTH_TEST);
 SceGuDepthMask (GU_FALSE);
 // turn off the cut-off mode of faces turned back to the viewer
 SceGuFrontFace (GU_CCW);
 SceGuDisable (GU_CULL_FACE);
 // set the opacity
 SceGuDisable (GU_BLEND);
 SceGuBlendFunc (GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0,0);
 // execute the created list
 SceGuFinish ();
 SceGuSync (GU_SYNC_WAIT, GU_SYNC_FINISH);
 SceGuDisplay (GU_TRUE);
 

After completing the GU operation, call sceGuTerm ().

After loading the texture size (WidthImage; HeightImage) in any convenient way (the Data pointer to the texture data – and it's better to get it in the video memory area), we can display it.

  // draw the scene
  SceGuStart (GU_DIRECT, DisplayList);
  // clear the screen and the depth buffer
  SceGuClearColor (0);
  SceGuClearDepth (0);
  SceGuClear (GU_COLOR_BUFFER_BIT | GU_DEPTH_BUFFER_BIT);

  // set up the projection matrix
  SceGumMatrixMode (GU_PROJECTION);
  SceGumLoadIdentity ();
  SceGumPerspective (EYE_ANGLE, SCREEN_ASPECT, NEAR_PLANE_Z, FAR_PLANE_Z);
  SceGumUpdateMatrix () ;.

  // initialize the matrices
  SceGumMatrixMode (GU_TEXTURE);
  SceGumLoadIdentity ();
  SceGumMatrixMode (GU_VIEW);
  SceGumLoadIdentity ();
  SceGumMatrixMode (GU_MODEL);
  SceGumLoadIdentity ();

  // output a rectangle with a texture
  SceGuColor (0xffffffff); // color of coloring
  SceGuEnable (GU_TEXTURE_2D);
  SceGuTexMode (GU_PSM_8888,0,0,0);
  SceGuTexImage (0, WidthImage, HeightImage, WidthImage, Data);
  SceGuTexFunc (GU_TFX_MODULATE, GU_TCC_RGBA);
  SceGuTexFilter (GU_NEAREST, GU_NEAREST);
  SceGuTexWrap (GU_REPEAT, GU_REPEAT);
  SceGuTexScale (1,1);
  SceGuTexOffset (0,0);
 
  // print the polygon by points from the array
  ...
    
  SceGuDisable (GU_TEXTURE_2D);
  // run the list to execute
  SceGuFinish ();
  SceGuSync (GU_SYNC_WAIT, GU_SYNC_FINISH);
  // make the buffer in which we drew visible
  SceDisplayWaitVblankStart ();
  SceGuSwapBuffers ();
 

How to remove the polygon? To draw GU geometry, PSP asks to place all the points in an array, the pointer to which should be obtained by the sceGuGetMemory command, passing the memory size in bytes to it. Further on this pointer you should write down the points and ask the PSP to print them, for example, with the sceGumDrawArray command. But what is the format of the points? In PSP, these points are arranged in a certain order and the size should be a multiple of 32 bytes: vertex weight, texture coordinates, point color, normal to the point, coordinate of the point. In order not to bother with the format, I defined a set of structures and functions for working with them:

  // # pragma pack (1)
/ [for vertices(1-8)] [weights (0-8)] [texture uv] [color] [normal] [vertex] [/for]

#pragma pack (1)

// point coordinate
Struct SGuVertex
{
 Float X;
 Float Y;
 Float Z;
};};
// normal to the point
Struct SGuNormal
{
 Float Nx;
 Float Ny;
 Float Nz;
};};
// texture coordinates
Struct SGuTexture
{
 Float U;
 Float V;
};};
// the color of the point
Struct SGuColor
{
 Unsigned long Color;
};};
#pragma pack ()

#pragma pack (32)
// point with texture, color, normal, coordinates
Struct SGuNVCTPoint
{
 SGuTexture sGuTexture;
 SGuColor sGuColor;
 SGuNormal sGuNormal;
 SGuVertex sGuVertex;
};};
#pragma pack ()

  Void SetVertexCoord (SGuVertex & sGuVertex, float x, float y, float z); // set the coordinates of the vertex
  Void SetNormalCoord (SGuNormal & sGuNormal, float nx, float ny, float nz); // set the coordinates of the normal
  Void SetTextureCoord (SGuTexture & sGuTexture, float u, float v); // set texture coordinates
  Void SetColorValue (SGuColor & sGuColor, unsigned long color); // set the color

// ------------------------------------------------ -------------------------------------------------- -
// set the coordinates of the vertex
// ------------------------------------------------ -------------------------------------------------- -
Void CMain :: SetVertexCoord (SGuVertex & sGuVertex, float x, float y, float z)
{
 SGuVertex.X = x;
 SGuVertex.Y = y;
 SGuVertex.Z = z;
}
// ------------------------------------------------ -------------------------------------------------- -
// set the coordinates of the normal
// ------------------------------------------------ -------------------------------------------------- -
Void CMain :: SetNormalCoord (SGuNormal & sGuNormal, float nx, float ny, float nz)
{
 SGuNormal.Nx = nx;
 SGuNormal.Ny = ny;
 SGuNormal.Nz = nz;
}
// ------------------------------------------------ -------------------------------------------------- -
// set texture coordinates
// ------------------------------------------------ -------------------------------------------------- -
Void CMain :: SetTextureCoord (SGuTexture & sGuTexture, float u, float v)
{
 SGuTexture.U = u;
 SGuTexture.V = v;
}
// ------------------------------------------------ -------------------------------------------------- -
// set the color
// ------------------------------------------------ -------------------------------------------------- -
Void CMain :: SetColorValue (SGuColor & sGuColor, unsigned long color)
{
 SGuColor.Color = color;
}
 

Then you can specify the geometry (in this case, a square), for example:

  // set the geometry
  SGuNVCTPoint sGuNVCTPoint;
  Vector  vector_point;

  SetVertexCoord (sGuNVCTPoint.sGuVertex, -100,100,0);
  SetTextureCoord (sGuNVCTPoint.sGuTexture, 0,0);
  SetNormalCoord (sGuNVCTPoint.sGuNormal, 0,0,1);
  SetColorValue (sGuNVCTPoint.sGuColor, 0xFFFFFFFF);
  Vector_point.push_back (sGuNVCTPoint);

  SetVertexCoord (sGuNVCTPoint.sGuVertex, 100,100,0);
  SetTextureCoord (sGuNVCTPoint.sGuTexture, 1.0);
  SetNormalCoord (sGuNVCTPoint.sGuNormal, 0,0,1);
  SetColorValue (sGuNVCTPoint.sGuColor, 0xFFFFFFFF);
  Vector_point.push_back (sGuNVCTPoint);

  SetVertexCoord (sGuNVCTPoint.sGuVertex, 100, -100.0);
  SetTextureCoord (sGuNVCTPoint.sGuTexture, 1,1);
  SetNormalCoord (sGuNVCTPoint.sGuNormal, 0,0,1);
  SetColorValue (sGuNVCTPoint.sGuColor, 0xFFFFFFFF);
  Vector_point.push_back (sGuNVCTPoint);

  SetVertexCoord (sGuNVCTPoint.sGuVertex, -100, -100.0);
  SetTextureCoord (sGuNVCTPoint.sGuTexture, 0.1);
  SetNormalCoord (sGuNVCTPoint.sGuNormal, 0,0,1);
  SetColorValue (sGuNVCTPoint.sGuColor, 0xFFFFFFFF);
  Vector_point.push_back (sGuNVCTPoint);
 

And to deduce it, for example, like this:

  size_t vertex_amount = vector_point.size ();
  SGuNVCTPoint * sGuNVCTPoint_Ptr = (SGuNVCTPoint *) sceGuGetMemory (vertex_amount * sizeof (SGuNVCTPoint));
  If (sGuNVCTPoint_Ptr! = NULL)
  {
   For (size_t n = 0; n <vertex_amount; n ++) sGuNVCTPoint_Ptr [n] = vector_point [n];
   SceGumDrawArray (GU_TRIANGLE_FAN, GU_COLOR_8888 | GU_VERTEX_32BITF | GU_TRANSFORM_3D | GU_NORMAL_32BITF | GU_TEXTURE_32BITF, vertex_amount, 0, sGuNVCTPoint_Ptr);
  }
 

And like everything works, but only if all the points are in front of the eyes and visible. As I understand it, the GU in PSP requires that in relation to the four clipping planes (left, right, top and bottom (and the front one will turn out automatically)) the point lies inside this volume. Otherwise, GU refuses to display it. Problem. Let's see how we solved this problem in PSP Quake 1.

Here's how:

  // we get the projection matrix
  SceGumMatrixMode (GU_PROJECTION);
  ScePspFMatrix4 projection_matrix;
  SceGumStoreMatrix (& projection_matrix);
  // get the matrix of the species transformation
  SceGumMatrixMode (GU_VIEW);
  ScePspFMatrix4 view_matrix;
  SceGumStoreMatrix (& view_matrix);
  // get the modeling matrix
  SceGumMatrixMode (GU_MODEL);
  ScePspFMatrix4 model_matrix;
  SceGumStoreMatrix (& model_matrix);
  SceGuFinish ();

  // calculate the common view-projection matrix
  ScePspFMatrix4 projection_view_matrix;
  MultiplyScePspFMatrix4 (view_matrix, projection_matrix, projection_view_matrix);
  // calculate the common matrix view-projection-model
  ScePspFMatrix4 projection_view_model_matrix;
  MultiplyScePspFMatrix4 (model_matrix, projection_view_matrix, projection_view_model_matrix);
  // calculate the view-model matrix
  ScePspFMatrix4 view_model_matrix;
  MultiplyScePspFMatrix4 (model_matrix, view_matrix, view_model_matrix);
  // calculate the four clipping planes by projection (top, bottom, left, right)
  ScePspFVector4 frustum [4]; // four numbers describe the plane: ax + by + cz + d = 0
  // left
  Frustum [0] .x = projection_view_model_matrix.x.w + projection_view_model_matrix.x.x;
  Frustum [0] .y = projection_view_model_matrix.y.w + projection_view_model_matrix.y.x;
  Frustum [0] .z = projection_view_model_matrix.z.w + projection_view_model_matrix.z.x;
  Frustum [0] .w = projection_view_model_matrix.w.w + projection_view_model_matrix.w.x;
  NormaliseScePspFVector4 (frustum [0]);
  // right
  Frustum [1] .x = projection_view_model_matrix.x.w-projection_view_model_matrix.x.x;
  Frustum [1] .y = projection_view_model_matrix.y.w-projection_view_model_matrix.y.x;
  Frustum [1] .z = projection_view_model_matrix.z.w-projection_view_model_matrix.z.x;
  Frustum [1] .w = projection_view_model_matrix.w.w-projection_view_model_matrix.w.x;
  NormaliseScePspFVector4 (frustum [1]);
  // upper
  Frustum [2] .x = projection_view_model_matrix.x.w-projection_view_model_matrix.x.y;
  Frustum [2] .y = projection_view_model_matrix.y.w-projection_view_model_matrix.y.y;
  Frustum [2] .z = projection_view_model_matrix.z.w-projection_view_model_matrix.z.y;
  Frustum [2] .w = projection_view_model_matrix.w.w-projection_view_model_matrix.w.y;
  NormaliseScePspFVector4 (frustum [2]);
  // lower
  Frustum [3] .x = projection_view_model_matrix.x.w + projection_view_model_matrix.x.y;
  Frustum [3] .y = projection_view_model_matrix.y.w + projection_view_model_matrix.y.y;
  Frustum [3] .z = projection_view_model_matrix.z.w + projection_view_model_matrix.z.y;
  Frustum [3] .w = projection_view_model_matrix.w.w + projection_view_model_matrix.w.y;
  NormaliseScePspFVector4 (frustum [3]);
 

In Quake 1, just move points inside the volume that restricts the view, or throw them at all (the whole figure is not visible). How to do it? Read the three matrices – GU_PROJECTION, GU_MODEL, GU_VIEW. Multiply them and get the resulting coordinate transformation matrix. From this matrix, you can pull out all the necessary bounding views of the plane (4 components of the resulting vector define a plane with the equation ax + by + cz + w = ​​0). (A, b, c) is the normal vector, and w = a * x0 + b * y0 + c * z0 – characterizes a certain point (x0, y0, z0) of the plane. The coordinates of the point are not needed for us – it is enough to know w.

The truncation is performed as follows (for the four above planes):

  // perform clipping
  Vector  vector_clip_point;
  For (long n = 0; n <4; n ++)
  {
   Float nx = frustum [n] .x;
   Float ny = frustum [n] .y;
   Float nz = frustum [n] .z;
   Float w = frustum [n] .w;

   Clip (vector_point, vector_clip_point, nx, ny, nz, w);
   Vector_point = vector_clip_point;
  }
 

For such a focus, we need the following functions (written off from Quake 1):

  // ---------------------------- -------------------------------------------------- ----------------------
// get the intersection point of the line and the plane
// ------------------------------------------------ -------------------------------------------------- -
Void CMain :: GetIntersectionPlaneAndLine (const SGuNVCTPoint & A, const SGuNVCTPoint & B, SGuNVCTPoint & new_point, float nx, float ny, float nz, float w)
{
 New_point = A;
 Float ax = A.sGuVertex.X;
 Float ay = A.sGuVertex.Y;
 Float az = A.sGuVertex.Z;
 Float au = A.sGuTexture.U;
 Float av = A.sGuTexture.V;

 Float bx = B.sGuVertex.X;
 Float by = B.sGuVertex.Y;
 Float bz = B.sGuVertex.Z;
 Float bu = B.sGuTexture.U;
 Float bv = B.sGuTexture.V;

 Float dx = bx-ax;
 Float dy = by-ay;
 Float dz = bz-az;
 Float du = bu-au;
 Float dv = bv-av;

 Float top = (nx * ax) + (ny * ay) + (nz * az) + w;
 Float bottom = (nx * dx) + (ny * dy) + (nz * dz);
 Float time = -top / bottom;

 Float vx = ax + time * dx;
 Float vy = ay + time * dy;
 Float vz = az + time * dz;
 Float vu = au + time * du;
 Float vv = av + time * dv;
 // add a new point
 SetVertexCoord (new_point.sGuVertex, vx, vy, vz);
 SetTextureCoord (new_point.sGuTexture, vu, vv);
}
// ------------------------------------------------ -------------------------------------------------- -
// perform coordinate correction
// ------------------------------------------------ -------------------------------------------------- -
Void CMain :: Clip (const vector  & vector_point_input, vector  & vector_point_output, float nx, float ny, float nz, float w)
{
 Vector_point_output.clear ();
 Long point = vector_point_input.size ();
 For (long n = 0; n  = point) next_p- = point;

  Const SGuNVCTPoint * sGuNVCTPoint_Current_Ptr = & (vector_point_input [n]);
  Float current_vx = sGuNVCTPoint_Current_Ptr-> sGuVertex.X;
  Float current_vy = sGuNVCTPoint_Current_Ptr-> sGuVertex.Y;
  Float current_vz = sGuNVCTPoint_Current_Ptr-> sGuVertex.Z;
  // determine the position relative to the clipping plane
  Float current_ret = current_vx * nx + current_vy * ny + current_vz * nz + w;

  Const SGuNVCTPoint * sGuNVCTPoint_Next_Ptr = & (vector_point_input [next_p]);
  Float next_vx = sGuNVCTPoint_Next_Ptr-> sGuVertex.X;
  Float next_vy = sGuNVCTPoint_Next_Ptr-> sGuVertex.Y;
  Float next_vz = sGuNVCTPoint_Next_Ptr-> sGuVertex.Z;
  // determine the position relative to the clipping plane
  Float next_ret = next_vx * nx + next_vy * ny + next_vz * nz + w;

  If (current_ret> 0) // the current point is visible
  {
   If (next_ret> 0) // the next point is visible
   {
    Vector_point_output.push_back (* sGuNVCTPoint_Next_Ptr);
   }
   Else
   {
    // add a new intersection
    SGuNVCTPoint sGuNVCTPoint_New;
    GetIntersectionPlaneAndLine (* sGuNVCTPoint_Current_Ptr, * sGuNVCTPoint_Next_Ptr, sGuNVCTPoint_New, nx, ny, nz, w);
    Vector_point_output.push_back (sGuNVCTPoint_New);
   }
  }
  Else // the current point is not visible
  {
   If (next_ret> 0) // the next point is visible
   {
    // add a new intersection
    SGuNVCTPoint sGuNVCTPoint_New;
    GetIntersectionPlaneAndLine (* sGuNVCTPoint_Current_Ptr, * sGuNVCTPoint_Next_Ptr, sGuNVCTPoint_New, nx, ny, nz, w);
    Vector_point_output.push_back (sGuNVCTPoint_New);
    // add the next point
    Vector_point_output.push_back (* sGuNVCTPoint_Next_Ptr);
   }
  }
 }
}
 

It's only after the clipping that the output of 3D graphics on the PSP with GU is correctly worked.

You can, by the way, use the vector PSP processor for scalar multiplication of vectors. For example, here is the function that determines whether clipping is required at all (by pieces from the same Quake 1 for PSP):

  // perform clipping
 Vector  vector_clip_point;
 // use the vector processor PSP
 __asm__ volatile
 (
  "Ulv.q C700,% 0  n" // load vector into register
  "Ulv.q C710,% 1  n" // load the vector into the register
  "Ulv.q C720,% 2  n" // load vector into register
  "Ulv.q C730,% 3  n" // load vector into register
  :: "m" (FrustumPlane [0]), "m" (FrustumPlane [1]), "m" (FrustumPlane [2]), "m" (FrustumPlane [3])
 );
 // check the need for clipping
 Long vertex = vector_point.size ();
 Bool clipping = false;
 For (long n = 0; n <vertex; n ++)
 {
  ScePspFVector4 current_vertex;
  Current_vertex.x = vector_point [n] .sGuVertex.X;
  Current_vertex.y = vector_point [n] .sGuVertex.Y;
  Current_vertex.z = vector_point [n] .sGuVertex.Z;
  Current_vertex.w = 1;
  Float ret1, ret2, ret3, ret4;
  __asm__ volatile
  (
   "Ulv.q C610,% 4  n" // load the vertex vector in the register
   "Vone.s S613  n" // put the unit in the fourth component of the vector
   "Vdot.q S620, C700, C610  n" // s620 = calculate the scalar product
   "Vdot.q S621, C710, C610  n" // s621 = calculate the scalar product
   "Vdot.q S622, C720, C610  n" // s622 = calculate the scalar product
   "Vdot.q S623, C730, C610  n" // s623 = calculate the scalar product
   "Mfv% 0, S620  n" // out1 = s620
   "Mfv% 1, S621  n" // out2 = s621
   "Mfv% 2, S622  n" // out3 = s622
   "Mfv% 3, S623  n" // out4 = s623
   : "= R" (ret1), "= r" (ret2), "= r" (ret3), "= r" (ret4): "m" (current_vertex)
  );
  If (ret1 <0 || ret2 <0 || ret3 <0 || ret4 <0) // clipping is required
  {
   Clipping = true;
   Break;
  }
 }
 

→ Link to the simplest application that displays the texture.

→ Link to the engine for the PSP using GU.

P.S. I know there are programmers here for PSP. Maybe they will tell why the PSP's GU is so arranged and how to properly work with it.

About the author

admin

Add Comment

Click here to post a comment

Your email address will not be published. Required fields are marked *