Sie befinden sich in den Archiven der Kategorie Einfache 3D Welten.
| M | D | M | D | F | S | S |
|---|---|---|---|---|---|---|
| « Jan | ||||||
| 1 | 2 | 3 | 4 | 5 | ||
| 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 |
| 27 | 28 | 29 | ||||
- Allgemein (6)
- Einfache 3D Welten (3)
- Erste Schritte (3)
- GU (4)
- Komplexe 3D Welten (1)
- 26.1.2011: Der Blog zieht um
- 23.3.2010: Der Anstrich - aber mit Plan
- 4.3.2010: 3D Landschaft - optimiert
- 17.2.2010: Landschaft 3D mit GU
- 28.1.2010: Ein Dreieck - GU Test
- 20.1.2010: Homebrew - 3D
- 12.1.2010: GU - und nu ?
- 11.1.2010: Neustart...
- 7.1.2010: Bildschirm aufräumen
- 5.1.2010: Schwarz/Weiß war gestern
PSP Allgemein
PSP Entwicklung
Archiv der Kategorie Einfache 3D Welten
Bildschirm aufräumen
7.1.2010 von AnMaBaGiMa.
Bisher haben wir immer wieder unsere Zeichenfläche mit der Landschaft voll”gemalt”. Das sah schonmal ganz gut aus, jedoch fällt uns auf, dass in dem Falle das am Horizont hohe Berge zu sehen waren und danach wieder flache die hohen einfach “stehen” bleiben. Das liegt daran, dass wir den Bildschirm nicht aufräumen - also leeren - bevor wir neu zeichnen.
Wenn wir dies nun einfach so einbauen, werden wir schnell feststellen, dass dies zum Teil zum Flackern kommt. Das sieht sehr unschön aus. Um das zu verhindern gibt es einen recht einfachen Trick:
Backbuffer
Oder wie auch immer man es nennen wil. Man verwendet quasi zunächst eine unsichtbare Zeichenfläche.Diese wird geleert und das neue Bild dargestellt/berechnet. Anschließend wird der fertige Bildschirm sichtbar gemacht. Nun wird wieder im “verborgenen” gezeichnet. Das führt dazu dass wir nun 2 Datenbereiche haben in die wir unser Spiel zeichnen und sagen der PSP am Ende nur noch wo der fertige Bildschirm liegt.
Zuerst definieren wir einen “flip” parameter vom Typ bool der immer anzeigt welcher Puffer denn nun der ist, in den wir zeichnen. Dann brauchen wir noch eine Methode die uns immer die Adresse auf den aktuellen Zeichenpuffer liefert, diese Adresse werden wir dann auch an die Render-Methode übergeben. Zum Schluss muss nur noch der Mainthread angepasst werden, so dass die Darstellung zwischen den 2 Puffern gewechselt wird.
Doch nun erstmal zu den neuen Definitionen in der Homebrew Basisklasse:
class ClHomebrew {
….
protected:
/*
* in welchen Puffer soll gezeichnet werden ?
*/
bool pg_drawframe;
/*
* ermitteln des adresse des zeichen Puffers
*/
unsigned char* pgGetVramAddr();
Die Implementierung für diese Methode sieht dann so aus:
unsigned char *ClHomebrew::pgGetVramAddr(){
if (pg_drawframe)
return VRAM_TOP + FRAMESIZE;
else
return VRAM_TOP;
}
Zusätzlich müssen wir den neuen Zeichenpuffer auch beim setzen der Bildpunkte berücksichtigen. Darum sieht die Methode setPixel nun wie folgt aus:
void ClHomebrew::setPixel(int x, int y, int color){
//Zeiger auf das zu zeichnende Pixel berechnen
unsigned char* screen = pgGetVramAddr()+x*PIXEL_SIZE+y*LINE_SIZE*PIXEL_SIZE;
//Farbe setzen
u32* s;
s = (u32*)screen;
*s = color;
}
Im nächsten Schritt überarbeiten wir nun den Mainthread unserer Voxelklasse. Dort holen wir uns erstmal die Adresse des Zeichenpuffers und setzen dort alle Daten auf 0 (also schwarz). Danach wird die Voxellandschaft gezeichnet und dann wird der Puffer dargestellt. Am Schluss folgt das setzen des Flags, dass wir nun in den 2. Puffer zeichnen wollen. Ggfl. müssen wir folgende Includes in unsere Voxelklasse mit aufnehmen:<stdlib.h> und <pspdisplay.h>
void ClPspVoxel::mainthread(){
unsigned char* drawBuffer;
drawBuffer = pgGetVramAddr();
//leer räumen des Bildschirms
memset(drawBuffer, 0, FRAMESIZE);
//Zeichnen
render();
//nun teilen wir der PSP mit, welches unser Zeichenpuffer ist der nun gezeigt werden soll
sceDisplaySetFrameBuf(drawBuffer, LINE_SIZE, PSP_DISPLAY_PIXEL_FORMAT_8888, PSP_DISPLAY_SETBUF_IMMEDIATE );
//Flip des schalters welcher Puffer gefüllt wird
pg_drawframe = pg_drawframe?false:true;
//nach jedem Zeichnungsvorgang darauf warten dass die PSP das Bild
//komplett ausgegeben hat
sceDisplayWaitVblankStart();
}
Geschrieben in Einfache 3D Welten | Drucken | Keine Kommentare »
Schwarz/Weiß war gestern
5.1.2010 von AnMaBaGiMa.
Heute ist Farbe angesagt. Darum werden wir nun ein paar Farbtöpfe auf unserer Landschaft “vergießen”. Zusätzlich wollen wir die Bewegungen über der Landschaft durch den PSP Analogstick beinflussen, verhindern, dass wir uns in der Landschaft durchgraben müssen und dann noch ein paar kleine Erweiterungen einbauen, die den Gesammteindruck verbessern…
Doch immer eins nach dem anderen.
Erstmal die Farbe
Für die Farbe auf der Landschaft brauchen wir eine Textur die die Landschaft farblich darstellt, da die Höhenkarte ja nur Grau-Werte - eben für die Höhe enthält. Ich verwende für die Höhenkarte eine passende Textur die von den Dimensionen die selben Maße hat (512×512) und wie folgt aussieht:

Die Textur müssen wir natürlich auch laden um diese benutzen zu können. Dazu werden wir die Methode, welche uns eine PNG Datei bereits in den Höhenkartenspeicher lädt erweitern und den Speicherplatz dynamisch zuweisen anstelle ein festes array zu definieren.
Die erweiterungen in der Klassendefinition:
class ClPspVoxel : public Cl2dHomebrew {
….
protected:
….
//neuer Parameter zur Aufnahme der gelesenen Daten
bool loadMap(const char* filename, unsigned int* data);
….
//Höhenkarte nun als Zeiger für dynamische Speicher Reservierung
//unsigned int mapData[512][512]; //Höhenwerte der Karte
unsigned int* mapData;
unsigned int* texture;
Die Anpassungen in der Implentierung der Lademethode sehen dann wie folgt aus:
/*
* Laden der Bilddaten aus PNG-File in Datenpuffer
* dazu wird die Vorlage aus der SDK graphics.c genutzt
* der Einfachheit halber gehen wir von Daten 512×512 punkten aus
*/
bool ClPspVoxel::loadMap(const char* filename, unsigned int* data) {
….
//Zeile nun Punkt für Punkt durchgehen und die Farbe in den Puffer übertragen
for (x = 0; x < width; x++) {
u32 color = line[x];
//neuer Zugriff auf den Datenpuffer in linearer Form ->zeile*maxBreite + spalte
//mapData[y][x] = color;
data[y*512 + x] = color;
}
Für die Anpassung der Initialisierungsmethode bei der wir nun den Speicher für die Höhenkarte und die Texture reservieren müssen wir noch das zusätzliche Include malloc.h einbinden.Der Code hierfür sieht dann so aus:
bool ClPspVoxel::init(){
…..
//Speicher für die Höhenkarte
mapData = (unsigned int*)malloc(sizeof(unsigned int)*512*512);
//Höhenkarte laden
loadMap(”altitude.png”, mapData);
//speicher für die Textur
texture = (unsigned int*)malloc(sizeof(unsigned int)*512*512);
loadMap(”landscape.png”, texture);
Um den dynamisch reservierten Speicher wieder freizugeben, wenn er nicht mehr gebraucht wird - das ist ganz wichtig! - platzieren wir folgende zeilen in den Klassendestruktor:
free(mapData); free(texture);
Um die Textur nun bei der Darstellung der Landschaft zu berücksichtigen müssen wir nur geringe Anpassungen in der render Methode vornehmen um auch die neue Art auf die Daten zuzugreifen zu berücksichtigen:
void ClPspVoxel::render() {
….
//Höhe des Voxel
altitude = mapData[voxelY*512 + voxelX]; //mapData[voxelY][voxelX];
//die Farbe dieses Voxel aus der textur lesen
unsigned int color = texture[voxelY*512 + voxelX];
……
// Punkt im Bildschirm
//setPixel(pixelX, pixelY, altitude);
setPixel(pixelX, pixelY, color);
Das Ergebnis sieht schon ganz ordentlich aus:

Nachdem wir die Landschaft mit ewas Farbe aufgepeppt haben, wollen wir die Bewegungen auf der PSP mit dem Analogstick kontrollieren. Um das zu erreichen brauchen wir in der Implementierungsklasse ersteinmal ein neues Include dass uns die Notwendigen Funktionen bereitstellt: pspctrl.h.
Nun können wir an das Ende der Bildbereichnung die Abfrage des PSP Pads legen:
void ClPspVoxel::render() {
….
//nach dem rendern fragen wir das pad ab.
//wurde der Analog-Stick bewegt ?
SceCtrlData pad;
sceCtrlPeekBufferPositive(&pad, 1);
//die ausrichtung des analog sticks ist
//0 ganz links, 255 ganz rechts
//0 ganz oben und 255 ganz unten.
//für eine Bewegung brauchen wir -x bis +x für links rechts
//und -y bis + y
short moveX, moveY;
moveX = pad.Lx - 127;
moveY = 127 - pad.Ly;
nbsp;
//da wir uns nicht im vollen “Auschlag” des Stix bewegen wollen, sondern
//maximal um 4 Pixel pro durchlauf verringern wir die Werte entsprechend
moveX >>= 5;
moveY >>= 5;
posY+=moveY;
posX+=moveX;
}
Schweben
Nun wollen wir noch über unsere Landschaft “schweben”. Dazu müssen wir die render Methode nur geringfügig anpassen. Bis jetzt ist die Höhe des Betrachters immer fest vorgegeben gewesen. Nun wird diese aber aus der Höhe des Punktes berechnet über dem sich der Betrachter gerade befindet.Zusätzlich bauen wir nach dem zeichnen der Bildpunkte in einer Spalte noch eine Sicherheitsabfrage ein, die verhindert, dass der Strahl weiter verfolgt wird, wenn der Bildschirm schon bis oben hin voll-”gemalt” wurde…
void ClPspVoxel::render() {
…..
rayZ = 81 + (mapData[(posY&511)*512 + (posX&511)] & 255); //384;
….
}while (rayZ < currentAltitude);
}
// wenn der Bildschirm am oberen Rand erreicht ist, brauchen
//wir den strahl nicht weiter verfolgen…
if (pixelY <= 1) break;
Viel Spaß bei den ersten “Flugversuchen”
Geschrieben in Einfache 3D Welten | Drucken | Keine Kommentare »
Ein kleiner Schritt
15.12.2009 von AnMaBaGiMa.
Wie eine einfache Voxellandschaft berechnet wird, habe ich nun kurz in der entsprechenden 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 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 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 ![]()
Geschrieben in Einfache 3D Welten | Drucken | Keine Kommentare »