OpenGL для PSP

Попробую описать основы использования OpenGL про программировании для PSP.

Использоваться будут следующие библиотеки: libGL, libGLU, libglut. libGL и libGLU — собственно для отрисовки графики, а libglut для того, чтобы не заморачиваться с базовыми вещами OpenGL, такими как создание окна. Версия прошивки на которой тестилось все это — 3.90-M33-3(4.01-M33-2)

Для примеров я использовал cygwin с установленным psptoolchain, подробнее об настройке см статью на хабре отличия в настройке роли играть не должны. Все вышеуказанные библиотеки как и SDK идут вместе с psptoolchain.

Рассматривать использование OpenGL и как написать HelloWorld в статье не буду, только приведу пример с комментариями. Подробнее читайте у NeHe 🙂

Примерный порядок действий приложения таков:

  1. Инициализация OpenGL
  2. установка размеров окна
  3. установка обработчиков различных событий: нажатия конпок, отрисовка сцены, изменение размера окна и т.д.
  4. запуск цикла приложения.

На самом деле на этом этапе все очень просто. Создаем main:

int main(int argc, char* argv[]){

    glutInit(&argc, argv); //инициализация OpenGL
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); //Установка режима окна
    glutInitWindowSize(480, 272); // установка размеров окна
    glutCreateWindow( __FILE__ ); // как ни странно - создание окна :)

    //установка обработчиков кнопок
    glutKeyboardFunc(keyPressed);
    glutKeyboardUpFunc(keyReleased); 

    //обработчики изменения размера окна и отрисовки сцены
    glutReshapeFunc(glSceneResize);
    glutDisplayFunc(glScene);

    // собственно запуск основного цикла программы
    glutMainLoop();

    return 0;
}

/* обработчик нажатия кнопки. */
void keyPressed(unsigned char key, int x, int y)
{
	switch (key) {
	case 'd':			/* delta, triangle */
		break;
	case 'o':			/* round */
		break;
	case 'q':			/* square*/
		break;
	case 'x':			/* cross */
		break;
	case 'n':			/* key with the music note */
		break;
	case 's':			/* select */
		break;
	case 'a':			/* startbutton */  /* If START is pressed, 
                                       kill everything. */
 		/* exit the program...normal termination. */
		exit(0);
	default:
		;
	}
 }

/* обработчик на отпускание кнопки. */
void keyReleased(unsigned char key, int x, int y)
{
	switch (key) {
	case 'd':			/* delta, triangle */
		break;
	case 'o':			/* round */
		break;
	case 'q':			/* square*/
		break;
	case 'x':			/* cross */
		break;
	case 'n':			/* key with the music note */
		break;
	case 's':			/* select */
		break;
	case 'a':			/* startbutton */
		break;
	default:
		;
	}
 }

// Обработчики resize и отрисовки сцены

GLvoid glScene(GLvoid){
    glTranslatef(-1.5f,0.0f,-6.0f);		// Move Left 1.5 Units 
                                                //And Into The Screen 6.0

	// draw a triangle
	glBegin(GL_POLYGON);	                // start drawing a polygon
	glVertex3f( 0.0f, 1.0f, 0.0f);		// Top
	glVertex3f( 1.0f,-1.0f, 0.0f);		// Bottom Right
	glVertex3f(-1.0f,-1.0f, 0.0f);		// Bottom Left
	glEnd();				// we're done with the polygon

	glTranslatef(3.0f,0.0f,0.0f);		// Move Right 3 Units

	// draw a square (quadrilateral)
	glBegin(GL_QUADS);			// start drawing a polygon (4 sided)
	glVertex3f(-1.0f, 1.0f, 0.0f);		// Top Left
	glVertex3f( 1.0f, 1.0f, 0.0f);		// Top Right
	glVertex3f( 1.0f,-1.0f, 0.0f);		// Bottom Right
	glVertex3f(-1.0f,-1.0f, 0.0f);		// Bottom Left
	glEnd();				// done with the polygon

	// swap buffers to display, since we're double buffered.
	glutSwapBuffers();
}

GLvoid glSceneResize(GLsizei Width, GLsizei Height)
{
        if (Height==0)          // Предотвращение деления на ноль, если окно слишком мало
            Height=1;

        glViewport(0, 0, Width, Height);        // Сброс текущей области вывода 
                                                // и перспективных преобразований

        glMatrixMode(GL_PROJECTION);            // Выбор матрицы проекций
        glLoadIdentity();                       // Сброс матрицы проекции

        gluPerspective(45.0f,(GLfloat)Width/(GLfloat)Height,0.1f,100.0f);
                // Вычисление соотношения геометрических размеров для окна
        glMatrixMode(GL_MODELVIEW);     // Выбор матрицы просмотра модели
}

Также нужно взять фаил psp-setup.c для установки всех необходимых для работы действий на pspsdk:

/**
 *  This file handles all the PSP-specific kernel setup and exit stuff.
 *
 *  Is there some general interest for this file, so that we can place it
 *  somewhere in the compiler toolchain include path?
 *
 *  Usage: Simply add
 *            -DMODULE_NAME="your-module-name" psp-setup.c
 *         to the LFLAGS or LDFLAGS of your project, so that this file is
 *         compiled in when gcc collects and links the final ELF binary.
 *
 *  Options:
 *         -DMODULE_NAME="name" -- set the name (default NONAME)
 *         -DMODULE_ATTR=0      -- module attributes (default 0)
 *         -DVERSION_MAJOR=1    -- version 1.x (default 1)
 *         -DVERSION_MINOR=0    -- version x.0 (default 0)
 *
 *  Note:  The linker flags and library lists need to be placed after this
 *         entry on the LFLAG or LDFLAGS command line, otherwise gcc won't
 *         be able to to resolve all symbols.
 */

#include <pspkerneltypes.h>
#include <pspuser.h>

#if !defined(MODULE_NAME)
	#define MODULE_NAME NONAME
#endif

#if !defined(MODULE_VERSION_MAJOR)
	#define MODULE_VERSION_MAJOR 1
#endif

#if !defined(MODULE_VERSION_MINOR)
	#define MODULE_VERSION_MINOR 0
#endif

#if !defined(MODULE_ATTR)
	#define MODULE_ATTR 0
#endif

#define __stringify(s)	__tostring(s)
#define __tostring(s)	#s

PSP_MODULE_INFO(__stringify(MODULE_NAME), MODULE_ATTR, 
                 MODULE_VERSION_MAJOR, MODULE_VERSION_MINOR);

static
int exit_callback (int arg1, int arg2, void *common)
{
	sceKernelExitGame();
	return 0;
}

static
int update_thread (SceSize args, void *argp)
{
	int cbid = sceKernelCreateCallback("Exit Callback", exit_callback, NULL);
	sceKernelRegisterExitCallback(cbid);
	sceKernelSleepThreadCB();
	return 0;
}

static void setup_callbacks (void) __attribute__((constructor));
static void setup_callbacks (void)
{
	int id;

	if ((id = sceKernelCreateThread("update_thread", 
                update_thread, 0x11, 0xFA0, 0, 0)) >= 0)

              sceKernelStartThread(id, 0, 0);
}

static void back_to_kernel (void) __attribute__((destructor));
static void back_to_kernel (void)
{
	sceKernelExitGame();
}

Домашнее задание — разобраться с тем, что делает вышеуказанный фаил 🙂

Пример Makefile для всего этого безобразия:

TARGET = lesson02
OBJS = lesson02.o

INCDIR =
CFLAGS = -O2 -G0 -Wall -DPSPFW3X
ASFLAGS = $(CFLAGS)

LIBDIR =
LDFLAGS =

EXTRA_TARGETS = EBOOT.PBP
PSP_EBOOT_TITLE = NeHe lesson 2

PSPSDK=$(shell psp-config --pspsdk-path)
PSPBIN = $(PSPSDK)/../bin
PSP_FW_VERSION=371
BUILD_PRX=1

CFLAGS += -I$(PSPSDK)/../include  -fsingle-precision-constant -g -Wall -O2
LIBS += -lglut -lGLU -lGL -lm -lc -lpsputility -lpspdebug -lpspge \
     -lpspdisplay -lpspctrl -lpspsdk -lpspvfpu -lpsplibc -lpspuser \
     -lpspkernel -lpsprtc -lpsppower
LDFLAGS += -DMODULE_NAME="lesson2" psp-setup.c
include $(PSPSDK)/lib/build.mak

В итоге после запуска на PSP, приложение должно показать белые квадрат и треугольник и выходить по кнопке Home 🙂

Передиралось из следующих источников:

Спасибо за внимание. Критику в каменты 🙂

~ от savant на 29 июня, 2008.

Оставьте комментарий