عرض رسومات ثلاثية الأبعاد على PSP

قبل شهرين ، أخرجت مرة أخرى PSP متربة من صندوق وقررت نقل محركي الذي أظهرته سابقًا هناك . لم تكن هناك مشاكل في عرض البرامج - كل شيء يعمل بهذه الطريقة. لكن استخدام GU لم يكن بهذه البساطة. في هذه المقالة ، سأوضح لك مثالًا لكيفية كتابة تطبيق بسيط ثلاثي الأبعاد لـ PSP باستخدام GU.

أحذرك مقدمًا من عدم وجود أدلة برمجة كافية لـ PSP ، وبالتالي قد تتضح أن بعض استنتاجاتي غير صحيحة. ولكن ، في صميم الموضوع.

الوظيفة الرئيسية لبرنامج PSP ، إذا كان أي شخص لا يعرف ، يبدو كما يلي:

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

تهيئة GU على النحو التالي:

أولاً ، نطلب مؤشرات إلى ثلاثة مخازن مؤقتة - حاجز للشاشة ، وخارج الشاشة ، ومخزن للعمق (Z-buffer). تتم محاذاة المخازن المؤقتة عند 512 بكسل لكل سطر (على الرغم من أن PSP يحتوي على خط 480 بكسل). تحتاج أيضًا إلى مراعاة تنسيق لون البكسل. في هذا المثال ، يتم استخدام التنسيق GU_PSM_8888 - 8 بت لكل مكون من مكونات R و G و B و Alpha بلون البكسل. بالنسبة إلى المخزن المؤقت Z ، يتم استخدام تنسيق GU_PSM_4444 ببساطة لأنه 16 بت - المخزن المؤقت Z 16 بت الخاص بـ 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); 

يتم تعريف وظيفة الاستعلام عن المؤشرات إلى المخازن المؤقتة على أنها

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

هذه ليست وظائفي - لقد أخذتها من برنامج ما منذ فترة طويلة ولم تتغير إلا قليلاً. يتم تخصيص الذاكرة في منطقة ذاكرة الفيديو. يجب أيضًا وضع القوام هناك إذا أمكن ، وطلب مؤشر من خلال getStaticVramTexture ، وإلا سينخفض ​​الأداء بشكل حاد. بالطبع ، لا يتم تخصيص ذاكرة ديناميكية أثناء هذه الطلبات ، ولكن ببساطة يتم تخصيص جزء من مساحة عنوان PSP المحددة للشاشة والقوام. بقدر ما أتذكر ، فإن PSP لا يحتوي إلا على 2 ميغابايت من ذاكرة الفيديو - وهي صغيرة جدًا لتخزين الكثير من الأنسجة.

تشبه برمجة PSP GU البرمجة الخاصة بـ OpenGL بفارق واحد - يتطلب تنفيذ الأوامر وضعها في قائمة العرض ، ويجب تخصيص ذاكرة هذه القائمة مسبقًا ومواءمتها:
حرف ثابت غير موقّع __صفة __ ((محاذاة (16))) DisplayList [262144] ؛
لا تتطلب الأوامر المتعلقة بتنسيق التحويل قائمة عرض ويمكن تنفيذها في أي مكان في البرنامج.

يمكنك تهيئة GU ، على سبيل المثال ، مثل:

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

بعد الانتهاء من العمل مع GU ، من الضروري استدعاء sceGuTerm ().

بعد تحميل نسيج الحجم (WidthImage ؛ HeightImage) بأي طريقة مناسبة (مؤشر البيانات إلى بيانات النسيج - ومن الأفضل الحصول عليه في منطقة ذاكرة الفيديو) ، يمكننا عرضه.

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

كيفية عرض مضلع؟ لرسم هندسة GU ، يطلب PSP وضع جميع النقاط في مصفوفة ، يجب الحصول على المؤشر أولاً باستخدام الأمر sceGuGetMemory ، بتمريره حجم كتلة الذاكرة المطلوبة بالبايت. بعد هذا المؤشر ، يجب كتابة مجموعة من النقاط واطلب من PSP عرضها ، على سبيل المثال ، باستخدام الأمر sceGumDrawArray مع المعلمات الضرورية. ولكن ما هو شكل هذه النقاط؟ بالنسبة لـ PSP ، يتم ترتيب بيانات النقاط بترتيب معين ويجب أن يكون حجم المصفوفة التي تصف نقطة واحدة مضاعفات 32 بايت: وزن الرأس ، إحداثيات النسيج ، لون النقطة ، عادي إلى نقطة ، إحداثيات نقطة. بهذا الترتيب. لكي لا تهتم بالتنسيق ، حددت مجموعة من الهياكل والوظائف للعمل معها:

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

ثم يمكنك تحديد الشكل الهندسي (في هذه الحالة ، المربع) ، على سبيل المثال ، مثل:

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

وإخراجها ، على سبيل المثال ، مثل هذا:

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

للإخراج ، أشرت إلى وظيفة sceGumDrawArray إلى ما أرسمه بالضبط وما هو تنسيق النقطة (GU_COLOR_8888 | GU_VERTEX_32BITF | GU_TRANSFORM_3D | GU_NORMAL_32BITF | GU_TEXTURE_32BITF - النقطة تتكون من اللون ، الإحداثيات ، العادي ، إحداثيات المصفوفة وتتطلب الضرب). الرسم ممكن فقط مع المثلثات. ولكن هذا ليس كل شيء ...

يبدو أن كل شيء يعمل ، ولكنه يعمل فقط إذا كانت جميع النقاط أمام العين ومرئية. بمجرد أن تصل نقطة واحدة على الأقل إلى مسافة ضبابية ، يرفض GU رسم المضلع بأكمله. كما أفهمها ، يتطلب GU's PSP ، بالنسبة إلى طائرات القطع الأربع (اليسار واليمين والجزء العلوي والسفلي (وستتحول الواجهة الأمامية تلقائيًا)) ، تكمن النقطة داخل هذا المجلد ، وإلا لن يوافق GU على عرضه. مشكلة. ولكن في الألعاب ، توجد رسومات ثلاثية الأبعاد ولا يتم ملاحظة مثل هذه القطع الأثرية! دعونا نرى كيف حلوا هذه المشكلة في PSP Quake 1 ، حيث أن المصادر متاحة للتحليل.

ماذا نرى من تحليل المصدر؟ لكن في الواقع ، هذا ما يلي:

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

أي في Quake 1 ، قبل الاستنتاج ، يقومون ببساطة بنقل جميع النقاط إلى داخل المجلد الذي يقيد العرض ، أو يطرحها تمامًا (إذا لم يكن الرقم بالكامل مرئيًا). كيف تفعل ذلك؟ تحتاج فقط إلى قراءة ثلاث مصفوفات - GU_PROJECTION ، GU_MODEL ، GU_VIEW. اضربهم واحصل على مصفوفة تحويل الإحداثيات النهائية. من هذه المصفوفة ، يمكنك سحب جميع الطائرات اللازمة لتقييد العرض (4 مكونات للمتجه الناتج تحدد مستوى مع فأس المعادلة + بواسطة + cz + w = ​​0). (a، b، c) هو المتجه العادي ، و w = a * x0 + b * y0 + c * z0 - يميز نقطة معينة (x0 ، y0 ، z0) للمستوى. نحن لسنا بحاجة إلى إحداثيات النقطة أنفسنا - فقط نعرف ث.

يتم تنفيذ القطع على النحو التالي (للطائرات الأربع المذكورة أعلاه بدورها في دورة):

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

ولكن من أجل هذا التركيز ، نحتاج إلى الوظائف التالية (خرجت من الزلزال 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); } } } } 

وفقط بعد إجراء مثل هذا القطع ، ستحصل أخيرًا بشكل صحيح أخيرًا على إخراج رسومات ثلاثية الأبعاد على PSP باستخدام GU. يمكنك إنشاء لعبة! :)



بالمناسبة ، يمكنك أيضًا استخدام معالج ناقل PSP للمنتج القياسي للمتجهات. على سبيل المثال ، فيما يلي وظيفة تحدد ما إذا كان القطع مطلوبًا على الإطلاق (ممزق إلى قطع من نفس Quake 1 لـ 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; } } 

كل شيء بسيط هنا - وضعوا ناقلات المستوى وإحداثيات النقطة في السجلات وطلبوا من VFPU أداء المنتج القياسي.

رابط إلى أبسط تطبيق عرض نسيج

ارتباط بمحرك PSP باستخدام GU

PS أعرف أن هناك مهنيين برمجة لـ PSP. ربما سيخبرونك عن سبب ترتيب PSP GU وكيفية التعامل معه بشكل صحيح.

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


All Articles