- PSP Spieleentwicklung - http://psp.anmabagima.de -

Ein kleiner Schritt

Dieser Eintrag stammt von AnMaBaGiMa Am 15.12.2009 @ 11:17 In Einfache 3D Welten | Keine Kommentare


Wie eine einfache Voxellandschaft berechnet wird, habe ich nun kurz in der entsprechenden [1] Seite ausgeführt. Doch wie bekommen wir das jetzt in die PSP ?
Bevor wieder etwas Code und Theorie sehr staubig daherkommen, sollen die folgenden Bilder den Appetit etwas anregen…
 

Die Implementierung

Nun aber los. Wir wollen das ganze in unsere Homebrew einbauen. Als Startpunkt nutzen wir unsere Feuer-Homebrew-Klasse. Die Dateien dieser Klasse kopieren wir im ersten Schritt und legen Sie unter dem Namen PspVoxel ab. Über die Refactoring-funktion in Eclipse benennen wir nun die Klasse von ClPspFire in ClPspVoxel um. Nun können alle Feuer spezifischen Definitionen gelöscht werden. Für unsere Landschaft brauchen wir folgende Attribute:

protected:
/*
* Datendeklarationen
*/
struct rayDelta {
long rayDeltaX, rayDeltaY;
} ;
rayDelta preDelta[480];
short posX; // Position X des Betrachters
short posY; // Position Y des Betrachters
unsigned int mapData[512][512]; //Höhenwerte der Karte
long mapSize; //breite der Karte

Im Bereich der Methoden werden wir nun eine neue Definieren. Diese soll es ermöglichen eine PNG Grafikdatei zu laden und als Höhenkarte zu verwenden. Der Einfachheithalber gehen wir zunächst einfach mal davon aus, dass dieses Bild immer 512×512 Bildpunkte hat.

protected:
/*
* laden eines PNG files als Höhenkarte
*/
bool loadMap(const char* filename);

Nun müssen nur noch die einzelnen Methoden implementiert werden. Zunächst die Initialisierung. Hier finden ein paar Vorberechnungen statt, dann initialisieren wir den Standpunkt des Betrachters auf der Höhenkarte und laden selbige in unseren Kartenpuffer. Der Vollständigkeithalber sind auch die Header-Dateien aufgeführt die wir benötigen als auch der Initialisierungsteil den wir von der Seite [2] Voxellandscape kennen.

/*
* PspVoxel.cpp
*/
#include “PspVoxel.h”
extern “C”{
#include <stdlib.h>
#include <pspdisplay.h>
#include <math.h>
#include <png.h>
}
bool ClPspVoxel::init(){
short x;
if (!ClHomebrew::init()) return false;
//in der Initialisierung einmal Hallo Voxel schreiben….
pspDebugScreenPrintf(”Hallo Voxel!”);   

long rayDeltaX, rayDeltaY;
float hx;
// Initiale Startposition in der Mitte der Karte
mapSize = 512;
posX = mapSize >> 1;
posY = mapSize >> 1;
for (x=0;x;lt;480;x++){
hx = x - 240;
rayDeltaX = ((hx / sqrtf(hx*hx + 400*400))*1024);
rayDeltaY = (400 / sqrtf(hx*hx + 400*400)*1024);
preDelta[x].rayDeltaX = rayDeltaX;
preDelta[x].rayDeltaY = rayDeltaY;
}
// Höhenkarte von Datei laden
loadMap(”altitude.png”);
return true;
}

Als nächstes bauen wir die Methode zum Rendern der Landschaft. Diese kann 1:1 aus der Seite [2] Voxellandscape entnommen werden und soll darum hier nicht nochmal aufgeführt werden. Im nächsten Schritt kommen die Anpassungen an dem Haupt-Thread dran:

void ClPspVoxel::mainthread(){
//zeichne Voxelspace
render();
//ein wenig Bewegung. Nach jedem “rendern” einen Schritt nach vorn laufen..
posY++;
//nach jedem Zeichnungsvorgang darauf warten dass die PSP das Bild
//komplett ausgegeben hat
sceDisplayWaitVblankStart();
}

Nun kommt der für diese Homebrew spannenste Teil - so glaube ich. Das laden einer PNG Grafik als Höhenkarte. Das PSP-SDK liefert hierzu eine gute Vorlage. In der Datei graphics.c ist die Implementierung zu finden. Wir werden diese jedoch nicht direkt aus dieser Datei nutzen sondern den Teil den wir brauchen mit kleineren Anpassungen in unsere Klasse implementieren. Warum machen wir das ? Die Graphics.c bietet weitaus mehr Funtkionen als wir brauchen und initialisiert für unseren aktuellen Bedarf etwas zu viel. Aber mit ein wenig copy+paste sieht die Laderoutine dann so aus:

/*
* Laden einer Höhenkarte aus PNG-File
* dazu wird die Vorlage aus der SDK graphics.c genutzt
*/
bool ClPspVoxel::loadMap(const char* filename) {
png_structp png_ptr;
png_infop info_ptr;
unsigned int sig_read = 0;
png_uint_32 width, height;
int bit_depth, color_type, interlace_type, x, y;
u32* line;
FILE *fp;
//öffnen der Datei
if ((fp = fopen(filename, “rb”)) == NULL) return false;
//lesen des PNG FileHeader
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL) {
fclose(fp);
return false;
}
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL) {
fclose(fp);
png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
return false;
}
png_init_io(png_ptr, fp);
png_set_sig_bytes(png_ptr, sig_read);
png_read_info(png_ptr, info_ptr);
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, int_p_NULL, int_p_NULL);
//zunächst sollte das Bild nicht größer als 512×512 Punkte sein…
if (width > 512 || height > 512) {
fclose(fp);
png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
return false;
}
png_set_strip_16(png_ptr);
png_set_packing(png_ptr);
if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png_ptr);
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8 ) png_set_gray_1_2_4_to_8(png_ptr);
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr);
png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
//speicherplatz zum lesen einer Zeile vom PNG file
line = (u32*) malloc(width * 4);
if (!line) {
fclose(fp);
png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
return false;
}
//PNG Zeile für Zeile lesen
for (y = 0; y < height; y++) {
png_read_row(png_ptr, (u8*) line, png_bytep_NULL);
//Zeile nun Punkt für Punkt durchgehen und die Farbe in den Puffer übertragen
for (x = 0; x < width; x++) {
u32 color = line[x];
mapData[y][x] = color;
}
}
free(line);
png_read_end(png_ptr, info_ptr);
png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
fclose(fp);
return true;
}

Nun haben wir im Grunde alles beisammen. Der letzte Schritt bevor wir diese Homebrew erstellen und testen können ist eine kleine Anpassung in der main.cpp, in der wir die Feuer-Klasse gegen die Voxelklasse austauschen, und in unserem Makefile. Durch die Nutzung der PNG und der Mathematischen Funtkionen brauchen wir 3 neue Bibliotheken die wir mit einbeinden müssen. Dies ist die png und z Bibliothek für die PNG Dateien und die math Bibliothek für die Funktionen wie sqrtf.
Das Makefile sieht damit wie folgt aus:

TARGET = pspdemo
OBJS = main.o PspVoxel.o Homebrew.o ../common/callbacks.o
INCDIR =
CFLAGS = -G0 -Wall -g
CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti
ASFLAGS = $(CFLAGS)
BUILD_PRX = 1
LIBDIR =
LDFLAGS =
LIBS= -lstdc++ -lpng -lz -lm
EXTRA_TARGETS = EBOOT.PBP
PSP_EBOOT_TITLE = PSP Demo
PSP_LARGE_MEM=1
PSP_FW_VERSION=500
PSPSDK=$(shell psp-config –pspsdk-path)
include $(PSPSDK)/lib/build.mak

Zwei weitere ggfl. Hilfreiche Hinweise:
Da wir nun durch das Laden einer Grafikdatei einen sehr großen Speicherbereich belegen werden, müssen wir das der PSP auch mitteilen. Dafür müssen wir zum einen im Makefile den Eintrag PSP_LARGE_MEM=1 platzieren - was in dem Auszug oben schon zu sehen ist. Zuätzlich muss in der main.cpp die Speicher Heap-Größe die genutzt werden soll auf einen “ungültigen” Wert gesetzt werden, um einen möglichst großen Block zugewiesen zu bekommen. Die Code Zeile sieht dann am Beginn der main.cpp so aus:

PSP_MODULE_INFO(”PSP Voxel”, 0, 1, 1);
PSP_MAIN_THREAD_ATTR(PSP_THREAD_ATTR_USER);
PSP_HEAP_SIZE_KB(-1024);// maximale Heap Größe nutzen

Und nun viel Erfolg beim testen und den ersten “Flugversuchen” über die Landschaft. Das ist ein kleiner Schritt für die PSP aber ein durchaus gewaltiger für den Entwickler ;)


Dieser Artikel wurde ausgedruckt ab PSP Spieleentwicklung: http://psp.anmabagima.de

URL zum Artikel: http://psp.anmabagima.de/2009/12/15/ein-kleiner-schritt/

URLs in this post:
[1] Seite: http://psp.anmabagima.de/digitale-landschaften-in-3d/voxellandscape/
[2] Voxellandscape: http://psp.anmabagima.de/?page_id=11
[3] Voxellandscape: http://psp.anmabagima.de/?page_id=11

Klicken hier zum Drucken.