И ещё вопрос тогда. Вот простейшее GU-приложение:
#include <pspkernel.h>
#include <pspdebug.h>
#include <pspctrl.h>
#include <pspdisplay.h>
#include <stdlib.h>
#include <pspgu.h>
#include <pspgum.h>
#include <math.h>
#ifndef bool
#define bool char
#define true 1
#define false 0
#endif
PSP_MODULE_INFO("SIMPLY GU MODE",0,1,1);
PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER | THREAD_ATTR_VFPU);
static unsigned int __attribute__((aligned(16))) list[262144];
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(void)
{
SceCtrlData pad;
pspDebugScreenInit();
SetupCallbacks();
sceCtrlSetSamplingCycle(0);
sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG);
sceKernelDcacheWritebackAll();
//---------------------------------------------
//по сути - это немного модифицированный OpenGL
//---------------------------------------------
//инициализируем графику GU
sceGuInit();
//запускаем на выполнение новый контекст дисплея - он должен выполниться сразу, т.к. GU_DIRECT
sceGuStart(GU_DIRECT,list);
//устанавливаем параметры буфера рисования- формат пикселя, указатель на область видеопамяти, длину строки (выровненную, а не физическую)
sceGuDrawBuffer(GU_PSM_8888,(void*)0,512);
//устанавливаем параметры буфера экрана - размер экрана, указатель на видеопамять, длину строки
sceGuDispBuffer(480,272,(void*)0x88000,512);
//устанавливаем параметры буфера глубины- указатель на начало буфера глубины в видеопамяти и длину строки
sceGuDepthBuffer((void*)0x110000,512);
//устанавливаем смещение экрана в общем пространстве 4096x4096 (в PSP такой размер виртуального экрана, судя по всему)
sceGuOffset(2048-(480/2),2048-(272/2));//ставим по центру
//настраиваем видовой порт - порт просмотра- координаты центра и размеры сторон
sceGuViewport(2048,2048,480,272);
//устанавливаем диапазон значений для буфера глубины - передняя и задняя плоскости отсечения (буфер инвертирован и значения от 0 до 65535 !)
sceGuDepthRange(65535,0);
//включаем обрезание области показа по размерам видового порта
sceGuScissor(0,0,480,272);
sceGuEnable(GU_SCISSOR_TEST);
//настроим матрицу проецирования
sceGumMatrixMode(GU_PROJECTION);
sceGumLoadIdentity();
sceGumPerspective(90.0f,16.0/9.0f,0.1f,100.0f);
//включим тест глубины
sceGuDepthFunc(GU_GEQUAL);
sceGuEnable(GU_DEPTH_TEST);
//включим режим гладкой интерполяции цвета граней
sceGuShadeModel(GU_SMOOTH);
//включим режим отсечения граней, повёрнутых обратной стороной к наблюдателю
sceGuFrontFace(GU_CW);
sceGuDisable(GU_CULL_FACE);
//очистим экран и буфер глубины
sceGuClearColor(0x00000000);
sceGuClearDepth(0);
sceGuClear(GU_COLOR_BUFFER_BIT|GU_DEPTH_BUFFER_BIT);
//инициализируем матрицы
sceGumMatrixMode(GU_VIEW);
sceGumLoadIdentity();
sceGumMatrixMode(GU_MODEL);
sceGumLoadIdentity();
//теперь можно рисовать
ScePspFVector3 ScePspFVector3_Working;
ScePspFVector3_Working.x=0;
ScePspFVector3_Working.y=0;
ScePspFVector3_Working.z=-10;
sceGumTranslate(&ScePspFVector3_Working);
//выбираем цвет
sceGuColor(0xffffffff);
//распределяем память в текущем дисплейном списке
//как только список выполнится, память освободится
//нельзя создавать массив вершин в обычной памяти, только в памяти списка или как глобальный массив!
//Иначе ничего изображено не будет.
float __attribute__((aligned(16))) *vertex=(float*)sceGuGetMemory(9*sizeof(float));
//треугольник задан в пространстве
vertex[0]=5;//x
vertex[1]=0;//y
vertex[2]=0;//z
vertex[3]=0;//x
vertex[4]=0;//y
vertex[5]=0;//z
vertex[6]=0;//x
vertex[7]=5;//y
vertex[8]=0;//z
//рисуем
sceGumDrawArray(GU_TRIANGLES,GU_VERTEX_32BITF|GU_TRANSFORM_3D,3,0,vertex);
//а вот так вот можно рисовать с координатами уже спроецированными на плоскость экрана
//выбираем цвет
sceGuColor(0xff00ffff);
vertex[0]=0;//x
vertex[1]=0;//y
vertex[2]=0;//z
vertex[3]=160;//x
vertex[4]=0;//y
vertex[5]=0;//z
vertex[6]=0;//x
vertex[7]=120;//y
vertex[8]=0;//z
//рисуем
sceGumDrawArray(GU_TRIANGLES,GU_VERTEX_32BITF|GU_TRANSFORM_2D,3,0,vertex);
//и выводим изображение
sceGuFinish();
//ждём, пока дисплейный список (у нас - контекст дисплея) не выполнится
sceGuSync(0,GU_SYNC_DONE);
//включаем дисплей
sceGuDisplay(GU_TRUE);
//делаем видимым буфер, в котором мы рисовали
sceGuSwapBuffers();
//ждём нажатия любой клавиши
while(!done)
{
sceCtrlReadBufferPositive(&pad, 1);
if (pad.Buttons!=0) break;
}
sceGuTerm();
sceKernelExitGame();
return(0);
}
Вопросы такие:
1) Как я понял, работает GU
всегда на основе дисплейных списков (как в OpenGL). И для списка нужно явно указывать доступный объём памяти да ещё и с выравниванием по параграфу. Почему-то все задают размер 262144 - это почему так? И вообще, сколько на борту видеопамяти?
2) Почему буфер глубины 16-ти битный? Точность же пострадает...
3) Какой смысл в виртуальном экране 4096x4096, если отображать можно 480x272 максимум и при этом переключать страницы sceGuSwapBuffers(). Зачем же тогда такой экран? Хм... Какой в нём смысл? Единственно, реализовывать полноэкранное сглаживание можно используя полный 4096x4096, но примимо ли это на PSP?
4) Нулевой байт цвета - это альфа-канал?
5) Почему примитивы можно создавать либо глобально в программе, либо распределяя память дисплейного списка командой float __attribute__((aligned(16))) *vertex=(float*)sceGuGetMemory(9*sizeof(float)), но нельзя выделить память типа float vertex[9]. У меня последний вариант не работает никак.
6) Зачем придумано обрезание порта просмотра командами
sceGuScissor(0,0,480,272);
sceGuEnable(GU_SCISSOR_TEST);
7) Для чего нужна команда sceGuDisplay(GU_TRUE); Не понимаю логики. Зачем включать то, что уже включено? Хм...
Вот такие вот пироги... я хорошо знаком с OpenGL, но вот GU меня местами удивляет неимоверно.
