Afficher des graphiques 3D sur PSP

Il y a quelques mois, j'ai de nouveau sorti une PSP poussiéreuse d'une boîte et j'ai décidé d'y porter mon moteur précédemment montré. Il n'y a eu aucun problème avec le rendu logiciel - tout fonctionne de cette façon. Mais utiliser GU n'était pas si simple. Dans cet article, je vais vous montrer comment vous pouvez écrire une application tridimensionnelle simple pour la PSP à l'aide du GU.

Je vous préviens à l'avance qu'il n'y a pas suffisamment de guides de programmation pour la PSP, et donc certaines de mes conclusions peuvent s'avérer incorrectes. Mais, au fait.

La fonction principale du programme pour la PSP, si quelqu'un ne le sait pas, cela ressemble Ă  ceci:

#include <pspkernel.h> #include <pspdebug.h> #include <pspdisplay.h> //---------------------------------------------------------------------------------------- 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); } //---------------------------------------------------------------------------------------- //  //---------------------------------------------------------------------------------------- int main(int argc, char **argv) { pspDebugScreenInit(); //  SetupCallbacks(); //  ………. //   sceKernelExitGame(); return(0); } 

L'initialisation du GU est la suivante:

Tout d'abord, nous demandons des pointeurs vers trois tampons - écran, hors écran et tampon de profondeur (Z-buffer). Les tampons sont alignés à 512 pixels par ligne (bien que la PSP ait une ligne de 480 pixels). Vous devez également tenir compte du format de couleur des pixels. Dans cet exemple, le format GU_PSM_8888 est utilisé - 8 bits par composants R, G, B et Alpha de la couleur du pixel. Pour le tampon Z, le format GU_PSM_4444 est utilisé simplement parce qu'il est de 16 bits - le tampon Z 16 bits de la PSP.

 //  #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); 

La fonction d'interrogation des pointeurs vers les tampons est définie comme

 #include <pspge.h> #include <pspgu.h> 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()))); } 

Ce ne sont pas mes fonctions - je les ai prises dans un programme il y a longtemps et je n'ai que légèrement changé. La mémoire est allouée dans la zone de mémoire vidéo. Les textures doivent également y être placées si possible, en demandant un pointeur via getStaticVramTexture, sinon les performances chuteront fortement. Bien sûr, aucune mémoire dynamique n'est allouée lors de telles requêtes, mais simplement une partie de l'espace d'adressage PSP spécifié est alloué pour l'écran et les textures. Pour autant que je me souvienne, la PSP ne dispose que de 2 mégaoctets de mémoire vidéo - elle est très petite pour stocker de nombreuses textures.

La programmation PSP GU est similaire à la programmation pour OpenGL avec une différence - l'exécution des commandes nécessite leur placement dans la liste d'affichage, et la mémoire de cette liste doit être pré-allouée et alignée:
caractère non signé statique __attribute __ ((aligné (16))) DisplayList [262144];
Les commandes liées à la transformation des coordonnées ne nécessitent pas de liste d'affichage et peuvent être exécutées n'importe où dans le programme.

Vous pouvez initialiser un GU, par exemple, comme ceci:

 //    PSP #define VIRTUAL_SCREEN_SIZE 2048 //   #define SCREEN_ASPECT 16.0f/9.0f //   #define NEAR_PLANE_Z 5.0f //   #define FAR_PLANE_Z 4096.0f //  #define EYE_ANGLE 60.0f //  GU sceGuInit(); //        -    , .. GU_DIRECT sceGuStart(GU_DIRECT,DisplayList); //   -  ,    ,   (,   ) sceGuDrawBuffer(GU_PSM_8888,fbp0,SCREEN_LINE_WIDTH); //    -  ,   ,   sceGuDispBuffer(SCREEN_WIDTH,SCREEN_HEIGHT,fbp1,SCREEN_LINE_WIDTH); //   -           sceGuDepthBuffer(zbp,SCREEN_LINE_WIDTH); //      4096x4096 ( PSP    ) sceGuOffset(VIRTUAL_SCREEN_SIZE-(SCREEN_WIDTH/2),VIRTUAL_SCREEN_SIZE-(SCREEN_HEIGHT/2));//   //   -  -      sceGuViewport(VIRTUAL_SCREEN_SIZE,VIRTUAL_SCREEN_SIZE,SCREEN_WIDTH,SCREEN_HEIGHT); //      -      (     0  65535 !) sceGuDepthRange(65535,0); //        sceGuScissor(0,0,SCREEN_WIDTH,SCREEN_HEIGHT); sceGuEnable(GU_SCISSOR_TEST); sceGuEnable(GU_CLIP_PLANES); //   sceGumMatrixMode(GU_PROJECTION); sceGumLoadIdentity(); sceGumPerspective(EYE_ANGLE,SCREEN_ASPECT,NEAR_PLANE_Z,FAR_PLANE_Z); //      sceGuShadeModel(GU_SMOOTH); //   sceGuDepthFunc(GU_GEQUAL); sceGuEnable(GU_DEPTH_TEST); sceGuDepthMask(GU_FALSE); //   ,      sceGuFrontFace(GU_CCW); sceGuDisable(GU_CULL_FACE); //  sceGuDisable(GU_BLEND); sceGuBlendFunc(GU_ADD,GU_SRC_ALPHA,GU_ONE_MINUS_SRC_ALPHA,0,0); //   sceGuFinish(); sceGuSync(GU_SYNC_WAIT,GU_SYNC_FINISH); sceGuDisplay(GU_TRUE); 

Après avoir terminé le travail avec GU, il est nécessaire d'appeler sceGuTerm ().

Après avoir chargé la texture de taille (WidthImage; HeightImage) de toute manière pratique (un pointeur de données vers les données de texture - et il est préférable de l’obtenir dans la zone de mémoire vidéo), nous pouvons l’afficher.

  //  sceGuStart(GU_DIRECT,DisplayList); //     sceGuClearColor(0); sceGuClearDepth(0); sceGuClear(GU_COLOR_BUFFER_BIT|GU_DEPTH_BUFFER_BIT); //   sceGumMatrixMode(GU_PROJECTION); sceGumLoadIdentity(); sceGumPerspective(EYE_ANGLE,SCREEN_ASPECT,NEAR_PLANE_Z,FAR_PLANE_Z); sceGumUpdateMatrix();. //  sceGumMatrixMode(GU_TEXTURE); sceGumLoadIdentity(); sceGumMatrixMode(GU_VIEW); sceGumLoadIdentity(); sceGumMatrixMode(GU_MODEL); sceGumLoadIdentity(); //    sceGuColor(0xffffffff);//  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); //      … sceGuDisable(GU_TEXTURE_2D); //    sceGuFinish(); sceGuSync(GU_SYNC_WAIT,GU_SYNC_FINISH); //  ,     sceDisplayWaitVblankStart(); sceGuSwapBuffers(); 

Comment afficher un polygone? Pour dessiner la géométrie GU, PSP demande de mettre tous les points dans un tableau, dont le pointeur doit d'abord être obtenu avec la commande sceGuGetMemory, en lui passant la taille du bloc de mémoire demandé en octets. Plus bas dans ce pointeur, vous devez écrire un tableau de points et demander à PSP de les afficher, par exemple, avec la commande sceGumDrawArray avec les paramètres nécessaires. Mais quel est le format de ces points? Pour PSP, les données de points sont organisées dans un ordre spécifique et la taille du tableau décrivant un point doit être un multiple de 32 octets: poids du sommet, coordonnées de texture, couleur du point, normal au point, coordonnée du point. Dans cet ordre. Afin de ne pas déranger avec le format, j'ai défini un ensemble de structures et de fonctions pour travailler avec eux:

 //#pragma pack(1) //[for vertices(1-8)] [weights (0-8)] [texture uv] [color] [normal] [vertex] [/for] #pragma pack(1) //  struct SGuVertex { float X; float Y; float Z; }; //   struct SGuNormal { float Nx; float Ny; float Nz; }; //  struct SGuTexture { float U; float V; }; //  struct SGuColor { unsigned long Color; }; #pragma pack() #pragma pack(32) //  , , ,  struct SGuNVCTPoint { SGuTexture sGuTexture; SGuColor sGuColor; SGuNormal sGuNormal; SGuVertex sGuVertex; }; #pragma pack() void SetVertexCoord(SGuVertex &sGuVertex,float x,float y,float z);//   void SetNormalCoord(SGuNormal &sGuNormal,float nx,float ny,float nz);//   void SetTextureCoord(SGuTexture &sGuTexture,float u,float v);//   void SetColorValue(SGuColor &sGuColor,unsigned long color);//  //---------------------------------------------------------------------------------------------------- //   //---------------------------------------------------------------------------------------------------- void CMain::SetVertexCoord(SGuVertex &sGuVertex,float x,float y,float z) { sGuVertex.X=x; sGuVertex.Y=y; sGuVertex.Z=z; } //---------------------------------------------------------------------------------------------------- //   //---------------------------------------------------------------------------------------------------- void CMain::SetNormalCoord(SGuNormal &sGuNormal,float nx,float ny,float nz) { sGuNormal.Nx=nx; sGuNormal.Ny=ny; sGuNormal.Nz=nz; } //---------------------------------------------------------------------------------------------------- //   //---------------------------------------------------------------------------------------------------- void CMain::SetTextureCoord(SGuTexture &sGuTexture,float u,float v) { sGuTexture.U=u; sGuTexture.V=v; } //---------------------------------------------------------------------------------------------------- //  //---------------------------------------------------------------------------------------------------- void CMain::SetColorValue(SGuColor &sGuColor,unsigned long color) { sGuColor.Color=color; } 

Ensuite, vous pouvez spécifier la géométrie (dans ce cas, le carré), par exemple, comme ceci:

  //  SGuNVCTPoint sGuNVCTPoint; vector<SGuNVCTPoint> 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); 

Et sortez-le, par exemple, comme ceci:

  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); } 

Pour la sortie, j'ai indiqué à la fonction sceGumDrawArray ce que je dessine exactement et quel est le format du point (GU_COLOR_8888 | GU_VERTEX_32BITF | GU_TRANSFORM_3D | GU_NORMAL_32BITF | GU_TEXTURE_32BITF - le point est composé de couleurs, de coordonnées, normales, de coordonnées de texture et de multiplication correspondantes). Le dessin n'est possible qu'avec des triangles. Mais ce n'est pas tout ...

Tout semble fonctionner, mais cela ne fonctionne que si tous les points sont devant les yeux et visibles. Une fois qu'au moins un point se trouve dans une certaine distance brumeuse, le GU refuse de dessiner le polygone entier. Si je comprends bien, le GU de la PSP exige que, par rapport aux quatre plans de détourage (gauche, droite, haut et bas (et le devant se révélera automatiquement)), le point se trouve à l'intérieur de ce volume, sinon le GU n'accepte pas de l'afficher. Problème. Mais dans les jeux, les graphismes 3D sont présents et de tels artefacts ne sont pas observés! Voyons comment ils ont résolu ce problème dans PSP Quake 1, car les sources sont disponibles pour analyse.

Que voyons-nous de l'analyse des sources? Mais en fait, c'est ce que:

  //   sceGumMatrixMode(GU_PROJECTION); ScePspFMatrix4 projection_matrix; sceGumStoreMatrix(&projection_matrix); //    sceGumMatrixMode(GU_VIEW); ScePspFMatrix4 view_matrix; sceGumStoreMatrix(&view_matrix); //   sceGumMatrixMode(GU_MODEL); ScePspFMatrix4 model_matrix; sceGumStoreMatrix(&model_matrix); sceGuFinish(); //   view-projection ScePspFMatrix4 projection_view_matrix; MultiplyScePspFMatrix4(view_matrix,projection_matrix,projection_view_matrix); //   view-projection-model ScePspFMatrix4 projection_view_model_matrix; MultiplyScePspFMatrix4(model_matrix,projection_view_matrix,projection_view_model_matrix); //  view-model ScePspFMatrix4 view_model_matrix; MultiplyScePspFMatrix4(model_matrix,view_matrix,view_model_matrix); //      (, , , ) ScePspFVector4 frustum[4];//   : ax+by+cz+d=0 // frustum[0].x=projection_view_model_matrix.x.w+projection_view_model_matrix.xx; frustum[0].y=projection_view_model_matrix.y.w+projection_view_model_matrix.yx; frustum[0].z=projection_view_model_matrix.z.w+projection_view_model_matrix.zx; frustum[0].w=projection_view_model_matrix.w.w+projection_view_model_matrix.wx; NormaliseScePspFVector4(frustum[0]); // frustum[1].x=projection_view_model_matrix.xw-projection_view_model_matrix.xx; frustum[1].y=projection_view_model_matrix.yw-projection_view_model_matrix.yx; frustum[1].z=projection_view_model_matrix.zw-projection_view_model_matrix.zx; frustum[1].w=projection_view_model_matrix.ww-projection_view_model_matrix.wx; NormaliseScePspFVector4(frustum[1]); // frustum[2].x=projection_view_model_matrix.xw-projection_view_model_matrix.xy; frustum[2].y=projection_view_model_matrix.yw-projection_view_model_matrix.yy; frustum[2].z=projection_view_model_matrix.zw-projection_view_model_matrix.zy; frustum[2].w=projection_view_model_matrix.ww-projection_view_model_matrix.wy; NormaliseScePspFVector4(frustum[2]); // frustum[3].x=projection_view_model_matrix.x.w+projection_view_model_matrix.xy; frustum[3].y=projection_view_model_matrix.y.w+projection_view_model_matrix.yy; frustum[3].z=projection_view_model_matrix.z.w+projection_view_model_matrix.zy; frustum[3].w=projection_view_model_matrix.w.w+projection_view_model_matrix.wy; NormaliseScePspFVector4(frustum[3]); 

Autrement dit, dans Quake 1, avant la conclusion, ils transfèrent simplement tous les points à l'intérieur du volume qui restreint la vue, ou les jettent complètement (si la figure entière n'est pas visible). Comment faire ça? Il vous suffit de lire trois matrices - GU_PROJECTION, GU_MODEL, GU_VIEW. Multipliez-les et obtenez la matrice de transformation de coordonnées finale. De cette matrice, vous pouvez retirer tous les plans nécessaires restreignant la vue (4 composantes du vecteur résultant définissent un plan avec l'équation ax + par + cz + w = ​​0). (a, b, c) est le vecteur normal, et w = a * x0 + b * y0 + c * z0 - caractérise un certain point (x0, y0, z0) du plan. Nous n'avons pas besoin des coordonnées du point nous-mêmes - sachez simplement w.

L'écrêtage s'effectue comme suit (pour les quatre plans susmentionnés tour à tour dans un cycle):

  //  vector<SGuNVCTPoint> 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; } 

Mais pour ce focus, nous avons besoin des fonctions suivantes (désaffectées de Quake 1):

 //---------------------------------------------------------------------------------------------------- //      //---------------------------------------------------------------------------------------------------- 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; //   SetVertexCoord(new_point.sGuVertex,vx,vy,vz); SetTextureCoord(new_point.sGuTexture,vu,vv); } //---------------------------------------------------------------------------------------------------- //   //---------------------------------------------------------------------------------------------------- void CMain::Clip(const vector<SGuNVCTPoint>& vector_point_input,vector<SGuNVCTPoint>& 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;n++) { long next_p=n+1; if (next_p>=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; //     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; //     float next_ret=next_vx*nx+next_vy*ny+next_vz*nz+w; if (current_ret>0)//   { if (next_ret>0)//   { vector_point_output.push_back(*sGuNVCTPoint_Next_Ptr); } else { //    SGuNVCTPoint sGuNVCTPoint_New; GetIntersectionPlaneAndLine(*sGuNVCTPoint_Current_Ptr,*sGuNVCTPoint_Next_Ptr,sGuNVCTPoint_New,nx,ny,nz,w); vector_point_output.push_back(sGuNVCTPoint_New); } } else//    { if (next_ret>0)//   { //    SGuNVCTPoint sGuNVCTPoint_New; GetIntersectionPlaneAndLine(*sGuNVCTPoint_Current_Ptr,*sGuNVCTPoint_Next_Ptr,sGuNVCTPoint_New,nx,ny,nz,w); vector_point_output.push_back(sGuNVCTPoint_New); //   vector_point_output.push_back(*sGuNVCTPoint_Next_Ptr); } } } } 

Et seulement après avoir effectué une telle coupure, vous recevez enfin enfin correctement la sortie des graphiques en trois dimensions sur la PSP en utilisant le GU. Vous pouvez créer un jeu! :)



Par ailleurs, vous pouvez également utiliser le processeur vectoriel PSP pour le produit scalaire des vecteurs. Par exemple, voici une fonction qui détermine si l'écrêtage est requis (déchiré en morceaux du même Quake 1 pour PSP):

 //  vector<SGuNVCTPoint> vector_clip_point; //   PSP __asm__ volatile ( "ulv.q C700, %0\n" //    "ulv.q C710, %1\n" //    "ulv.q C720, %2\n" //    "ulv.q C730, %3\n" //    :: "m"(FrustumPlane[0]),"m"(FrustumPlane[1]),"m"(FrustumPlane[2]),"m"(FrustumPlane[3]) ); //   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" //      "vone.s S613\n" //       "vdot.q S620, C700, C610\n" // s620 =    "vdot.q S621, C710, C610\n" // s621 =    "vdot.q S622, C720, C610\n" // s622 =    "vdot.q S623, C730, C610\n" // s623 =    "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=true; break; } } 

Tout est simple ici - ils ont placé les vecteurs plans et les coordonnées ponctuelles dans les registres et ont demandé au VFPU de réaliser le produit scalaire.

→ Lien vers l'application d'affichage de texture la plus simple

→ Lien vers le moteur PSP à l'aide de GU

PS Je sais qu'il existe des professionnels de la programmation pour PSP. Peut-être qu'ils vous diront pourquoi le GU PSP est ainsi organisé et comment l'utiliser correctement.

Source: https://habr.com/ru/post/fr406049/


All Articles