Sie befinden sich in den Archiven der Kategorie GU.
| 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 GU
Der Anstrich - aber mit Plan
23.3.2010 von AnMaBaGiMa.
Die Drahtgitterlandschaften sind fertig gestellt - egal mit welcher Methode - und nun geht es daran auch dieser wieder etwas Farbe einzuhauchen. Dazu bedient man sich sogenannter Texturen. Das sind Bilder einer beliebigen Oberfläche die auf das virtuelle 3D Objekt gelegt werden. Dadurch erscheinen sie realer und plastischer, als wenn Sie einfach nur mit einer einfachen Farbe angestrichen wurden.
Texturen verwalten
Immer wieder , wenn wir 3D szenen auf der PSP darstellen wollen, werden wir die verschiedensten Texturen zum Einsatz bringen. Da macht es Sinn sich darüber Gedanken zu machen, wie wir diese am besten in unserer Homebrew verwalten. Die GU der PSP bringt hier “leider” keine Verwaltungsfunktionen mit. Darum werden wir hier eine sehr einfache Variante in Form einer Texterverwaltungsklasse selber erstellen. Ein paar Grundüberlegungen:
1. die Texturen sollen zunächst nur von PNG Bilddateien gelesen werden
2. ein und das selbe Bild soll als Textur nur einmal geladen werden
3. geladene Texturen sollen individuell wieder freigegeben werden können um Speicher zu sparen
4. die Texturen sollen ge-swizzled werden
5. die Klasse soll einfach erweiterbar sein, zBsp. um Laderoutinen für JPG oder andere Bildformate
Dies sind ein paar gute Rahmenbedingungen. Machen wir uns nun ans Werk. Wir erstellen eine neue Klasse mit dem Namen ClTextureMgr. Im Header definieren wir zunächst ein paar Beschränkungen, wie die maximale Größe einer Textur, die sich auf 512×512 Bildpunkte beschränkt (mehr kann die GU nicht verarbeiten). Zusätzlich eine Struktur, in der alle wichtigen Daten einer Textur die wir verwalten wollen abgelegt sind.
/*
* TextureMgr.h
*/
#ifndef TEXTUREMGR_H_
#define TEXTUREMGR_H_
// Maximale größe einer Textur und anzahl Texturen die verwaltet werden sollen
#define TEX_MAX_WIDTH (512)
#define TEX_MAX_HEIGHT (512)
#define MAX_TEX_COUNT 100
/*
* Definition einer Struktur die alle daten einer Textur enthält
* die hier verwaltet werden soll
*/
typedef struct Texture{
unsigned int id; //ID der Textur
char* name; //Name der Textur/Bilddatei
int type; //Typ der Textur - siehe GU_PSM* Enums
int width, height; //Breite und Höhe der Textur als Potenz von 2
int stride; //Pufferbreite für Zugriff auf Texturdaten als Potenz von 2
bool swizzled; //WAHR wenn Texturdaten ge-swizzled sind
void* data; //Zeiger auf die Bilddaten der Textur
}Texture;
Nun folgt die eigentliche Definition der Klasse. Sie wird als Singleton ausgeprägt und kann somit nicht direkt instanziiert werden. Dies vereinfacht die Verwaltungsaufgaben der Klasse. Es müssen also 2 statische Methoden zum holen und zum abräumen der Singleton-Instanz erzeugt werden. Alle weiteren Methoden werden dann Instanz basiert ausgeprägt. Die Definition sieht dann folgendermaßen aus.
class ClTextureMgr {
public:
/*
* Statische Methoden für den Singleton Zugriff
*/
static ClTextureMgr* getInstance();
static void freeInstance();
/*
* Laden einer Textur aus einer PNG Datei
*/
Texture* loadFromPNG(const char* filename);
/*
* Liefert die verwaltete Textur anhand der ID
*/
Texture* getTexture(unsigned int id);
/*
* Freigeben der Ressorcen für eine Textur
*/
void freeTexture(unsigned int id);
protected:
/*
* Texturdaten "swizzlen"
*/
void swizzle(unsigned char* out, unsigned char* in, unsigned int width, unsigned int height);
/*
* Konstruktor ist protected um direkte Instanziierung zu verhindern
*/
ClTextureMgr();
virtual ~ClTextureMgr();
/*
* es folgen die Member-Variablen zur Klasse
*/
private:
static ClTextureMgr* _instance; //die singleton Instanz
int managedTextures; //Anzahl verwalteter Texturen
Texture* textures; //Liste aller verwalteten Texturen
};
#endif /* TEXTUREMGR_H_ */
Kommen wir nun zur Implementierung der Klasse. Den Start bildet hier eine kleine - von der Klasse losgelöste - Hilfsroutine um von einer gegebenen Zahl die nächst höhere Zahl als Potenz von 2 zu bestimmen (das haben wir schonmal gesehen
). Daneben sind die statischen Methoden für die Verwendung der Klasse als Singleton implementiert.
/*
* TextureMgr.cpp
*/
//die notwendigen Includes
extern "C"{
#include <malloc.h>
#include <stdlib.h>
#include <png.h>
#include <psptypes.h>
#include <pspgu.h>
}
#include "TextureMgr.h"
/*
* eine einfache Hilfsfunktion. Lokal und statisch...
*/
static int getNextPower2(int width)
{
int b = width;
int n;
for (n = 0; b != 0; n++) b >>= 1;
b = 1 << n;
if (b == 2 * width) b >>= 1;
return b;
}
//Am Anfang ist die Singleton instanz nicht erzeugt
ClTextureMgr* ClTextureMgr::_instance = 0;
ClTextureMgr::ClTextureMgr() {
//der Konstruktor initialisiert die Datan
this->managedTextures = 0;
textures = (Texture*)malloc(MAX_TEX_COUNT*sizeof(Texture));
}
ClTextureMgr *ClTextureMgr::getInstance()
{
if (!_instance) {
_instance = new ClTextureMgr();
}
return _instance;
}
void ClTextureMgr::freeInstance(){
if (_instance){
delete(_instance);
}
}
Der nächste Schritt ist die Methode zum laden eines PNG Files als verwaltete Textur. Da bereits an vielen anderen Stellen das laden der PNG Datei beschrieben und auch als Code dargestellt wurde, und weil der original Code Teil des PSPSDK ist, werde ich diesen hier aussparen, wohl aber den für den Texturverwaltungsteil wichtigen Part zeigen.
Texture *ClTextureMgr::loadFromPNG(const char *filename){
//Befor wir das File laden, prüfen ob es schon eine Textur
//mit dem selben Namen gibt, wenn ja eben diese Textur zurück liefern
for (short t=0;t<managedTextures;t++){
if (textures[t].name){
if (strncmp(filename, textures[t].name, strlen(filename))==0){
return &textures[t];
}
}
}
//es ist wirklich eine neue Textur, also diese Laden und die Daten füllen
managedTextures++;
if (managedTextures >= MAX_TEX_COUNT) return NULL;
// get the pointer of the new texture data structure
Texture* newTex = &textures[managedTextures-1];
newTex->name = (char*)malloc(strlen(filename));
strcpy(newTex->name, filename);
/*********************************************************
* hier erfolgt nun das Laden des PNG Files und das Ablegen der Daten im
* Speicher für diese Textur, sollte hier ein Fehler passieren wird NULL zurück-
* gegeben.
*********************************************************/
//vorbereiten für die Rückgabe der Textur
newTex->id = managedTextures;
newTex->swizzled = false;
//veruche die Texturdaten zu swizzlen
//Speicher für die neuen Daten besorgen
void* swizzleData = malloc(sizeof(u32)*newTex->stride*newTex->height);
if(swizzleData){
swizzle((unsigned char*)swizzleData, (unsigned char*)newTex->data, newTex->stride*sizeof(u32), newTex->height);
newTex->swizzled = true;
//Speicher der originalen Daten freigeben
free(newTex->data);
newTex->data = swizzleData;
} else
newTex->swizzled = false;
return newTex;
}
Swizzle
Das nun schon oft erwähnte Wort bedeutet direkt übersetzt: Bauernfängerei
bzw. mit dem Zusatz “Stick” bedeutet es Rührstab. Letzteres trifft hier eher zu. Die Daten der Textur sind im Normalfall ja in einem Datenpuffer ordentlich in Reihenfolge der Bildpunkte abgelegt. Des Swizzle-Verfahren rührt diese Bildpunkte so “durcheinander” dass die Daten nicht mehr so abgelegt sind wie sie auf dem Bildschirm zu sehen sind, sondern optimal von der GU zugegriffen werden können. Das ganze bringt einen großen Geschwindigkeitsgewinn beim Arbeiten mit Texturen.
Eine leicht lesbare - nicht auf Performance getrimmte - Swizzle-Routine wird in unserer Textur-Verwaltungsklasse verwendet:
void ClTextureMgr::swizzle(unsigned char* out, unsigned char* in, unsigned int width, unsigned int height)
{
unsigned int i,j;
unsigned int rowblocks = (width / 16);
for (j = 0; j < height; ++j)
{
for (i = 0; i < width; ++i)
{
unsigned int blockx = i / 16;
unsigned int blocky = j / 8;
unsigned int x = (i - blockx*16);
unsigned int y = (j - blocky*8);
unsigned int block_index = blockx + ((blocky) * rowblocks);
unsigned int block_address = block_index * 16 * 8;
out[block_address + x + y * 16] = in[i+j*width];
}
}
}
Abschließend und der Vollständigkeit halber die Methode um eine verwaltete Textur einer bestimmten ID zurückzuliefern, eine Textur freizugeben und den Klassendestruktor.
Texture *ClTextureMgr::getTexture(unsigned int id){
//Prüfen das die ID auch valide ist
if (id > managedTextures){
return NULL;
}
return &textures[id-1];
}
void ClTextureMgr::freeTexture(unsigned int id){
Texture* text = &textures[id-1];
if (text){
free(text->data);
free(text->name);
text->data = 0;
text->name = 0;
}
}
ClTextureMgr::~ClTextureMgr() {
//Allen reservierten Speicher freigeben
for (int t=0;t<managedTextures;t++){
if (textures[t].data) free(textures[t].data);
if (textures[t].name) free(textures[t].name);
}
if (managedTextures)
free(textures);
}
Geschrieben in GU | Drucken | Keine Kommentare »
Ein Dreieck - GU Test
28.1.2010 von AnMaBaGiMa.
Die GU Bibliothek zur einfachen Erstellung unser ersten 3D Homebrew ist fertig und nun wollen wir natürlich einen ersten kleinen Test durchführen um - zunächsteinmal nur ein Dreieck - im virtuellen Raum auf den realen PSP Bildschirm zu zaubern.
Die in der PspHbc Bibliothek enthaltenen Header Files entpackt ihr am besten in euer PSPSDK Verzeichnis C:\pspsdk\psp\sdk\include das Lib-File kommt dann in das Verzeichnis C:\pspsdk\psp\sdk\lib dadurch wird die Nutzung in eigenen Projekten stark vereinfacht.
Nun aber los. Wie schon bei unserer ersten “normalen” Homebrew beginnt alles mit dem erstellen einer neuen Klasse. Diese wird nun von der 3DHomebrew-Basisklasse abgeleitet. Unsere Beispiel Homebrew soll den Namen HbcGuSample tragen. Die Klasse heißt dann treffender Weise: ClHbcGuSampleApp
/*
* HbcGuSampleApp.h
* Beispiel Implementierung der GU Homebrew Basisklasse
*/
#ifndef HBCGUSAMPLEAPP_H_
#define HBCGUSAMPLEAPP_H_
#include <3dHomebrew.h>
class ClHbcGuSampleApp : public Cl3dHomebrew {
public:
static ClHbcGuSampleApp* getInstance();
static void releaseInstance();
// Redefiniren der Render Methode um sie mit eigender Logik zu füllen
void render();protected: ClHbcGuSampleApp();
virtual ~ClHbcGuSampleApp();
static ClHbcGuSampleApp* _instance;
};
#endif /* HBCGUSAMPLEAPP_H_ */
Nachdem die neue Klasse nun definiert ist, können wir diese implementieren. Dabei ist der erste Teil der Implementierung recht einfach - glaube ich - da wir nur eine singleton Instanz erzeugen und diese zurück liefern. Die GU initializeirung bleibt unverändert.
/*
* HbcGuSampleApp.cpp
*
*/
extern “C”{
#include<pspgu.h>
#include<pspgum.h>
}
#include “HbcGuSampleApp.h”
ClHbcGuSampleApp* ClHbcGuSampleApp::_instance = 0;
// liefert singleton Instaz
ClHbcGuSampleApp *ClHbcGuSampleApp::getInstance(){
if(!_instance){
_instance = new ClHbcGuSampleApp();
}
return _instance;
}
//baut die singleton Instanz wieder ab und gibt Resourcen frei
void ClHbcGuSampleApp::releaseInstance(){
if (_instance) {
delete(_instance);
_instance = 0;
}
}
Der für die Klasse generierte Constructor und Destructor bleiben unberührt bzw. leer. Der nun “spannende” Teil befindet sich in der render Methode. Hier wollen wir unser erstes Objekt im virtuellen Raum definieren.
Wichtig:Definitionen von Objekten, Bewegungen und Objektrelationen im 3 dimensionalen Raum nutzen Vektoren und Matrizen. Wer also keinerlei Grundkenntnisse auf diesem Gebiet mitbringt sollte sich diese aneignen befor er hier weiter macht. Wikipedia ist eine gute Addresse um sich diesbezüglich aufzuschlauen. Oftmals helfen einem bei der Veranschaulichung auch ein paar Bleistifte (als Vektoren) und ein Tisch aus.
Das Dreieck
Das Dreick wird durch 3 Punkte im virtuellen Raum beschrieben. Diese Punkte erhalten zusätzlich zu ihrer Position noch einen Farbwert, so dass die Fläche die von dem Dreieck umspannt wird entsprechend ausgefüllt wird. Dabei verwenden wir hier für jeden Punkt einen anderen Farbwert um einen netten Farbverlauf zu erzeugen.
void ClHbcGuSampleApp::render(){
// Definition einer Struktur die einen 3D-Punkt beschreibt
typedef struct Vertex {
int color;
float x, y, z;
}Vertex;
//Speicher für 3 Punkte reservieren
//dieser Speicher ist dabei nur temporär und nur innerhalb der aktuellen GU Liste gültig
Vertex* triangle = (Vertex*)sceGuGetMemory(sizeof(Vertex)*3);
//Daten für die Punkte
triangle[0].x = -4.0f;
triangle[0].y = -2.0f;
triangle[0].z = -10.0f;
triangle[0].color = 0xff0000ff;triangle[1].x = 0.0f;
triangle[1].y = 2.0f;
triangle[1].z = -10.0f;
triangle[1].color = 0xffff0000;triangle[2].x = 4.0f;
triangle[2].y = -2.0f;
triangle[2].z = -10.0f;
triangle[2].color = 0xff00ff00;
Als nächsten Schritt wollen wir dieses Dreieck auch zeichnen. Zuerst leeren wir den Bildschirm mit einer von schwarz verschiedenen Farbe (schwarz macht’s die Basisklasse). Dadurch kann mann schneller erkennen ob hier was “gezeichnet” wird oder nicht, da ggfl. schwarz auf schwarz gezeichnete Objekte nicht sichtbar wären…
Nachdem wir den Bildschirm nochmals geleert haben müssen wir die Projektion der GU festlegen. Dafür legen wir für den aktuellen View und für das Objektmodell eine Einheitsmatrix fest. Dies bedeutet, dass weder unsere Kamera, noch unser Objekt im Raum manipuliert werden. Sie werden so dargestellt wie wir sie im Raum platziert haben. Mit der Model-Matrix wären wir z.Bsp. in der Lage das Objekt im Raum zu bewegen oder zu rotieren, ohne die ursprüngliche Definition der Punkte zu verändern. Doch das beleuchten wir zu einem späteren Zeitpunkt.
Doch nun zum Code:
//den Bildschirm mit einer eigenen Farbe leeren, anders als in 3DHomebrew sceGuClearColor(0xff442222); sceGuClear(GU_COLOR_BUFFER_BIT);// Festlegen der View und Model Matrix als Einheitsmatrix sceGumMatrixMode(GU_VIEW); sceGumLoadIdentity(); sceGumMatrixMode(GU_MODEL); sceGumLoadIdentity();//damit die Farbwerte beim Füllen des Dreiecks verlaufen “smooth” (weiche/fortlaufende) Schattierung aktivieren sceGuShadeModel(GU_SMOOTH); //Das Dreieck kommt ganz ohne Texturen aus…also deaktivieren sceGuDisable(GU_TEXTURE_2D); // hier wird das Dreieck dann tatsächlich auf den Bildschirm gebracht, oder // besser in den Zeichenpuffer der beim nächsten wechsel zwischen Display- und Zeichenpuffer // sichtbar wird sceGumDrawArray(GU_TRIANGLES, GU_TRANSFORM_3D | GU_VERTEX_32BITF | GU_COLOR_8888, 3, 0, triangle); }
Ganz zum Schluss - bevor wir dieses Beispiel nun erstellen können müssen wir in unserem Makefile noch einige Bibliotheken aufnehmen, damit wir die GU Funktionen auch nutzen können. Im Detail sieht dieses dann folgendermaßen aus:
TARGET = hbcGuSample OBJS = main.o HbcGuSampleApp.o INCDIR = CFLAGS = -G0 -Wall -g CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti ASFLAGS = $(CFLAGS) BUILD_PRX = 1 LIBDIR = LDFLAGS = LIBS = -lstdc++ -lpsphbc -lpspgum -lpspgu -lpng -lz -lm EXTRA_TARGETS = EBOOT.PBP PSP_EBOOT_TITLE = HBC Gu Sample PSP_LARGE_MEM=1 PSP_FW_VERSION=500 PSPSDK=$(shell psp-config --pspsdk-path) include $(PSPSDK)/lib/build.mak
Wenn nun die Erstellung dieses Beispiels glatt gelaufen ist, dann sollte das ganze auf der PSP so aussehen:

Geschrieben in GU | Drucken | Keine Kommentare »
Homebrew - 3D
20.1.2010 von AnMaBaGiMa.
Die ersten Grundlagen für die GU habe ich zusammengestellt und die Initialisierung ist klar. Ähnlich wie am Anfang werde ich nun eine Homebrew-Basisklasse bauen die die Initialisierung der GU durchführt und von der die eigentliche Applikations-/Spielklasse abgeleitet werden kann.
Da die Grundlegende Homebrew Einrichtung (mit den Callbacks etc) bereits in einer Klasse zusammengefasst ist, werden wir diese einfach ableiten und eine neue Klasse Cl3dHomebrew erstellen:
#include “Homebrew.h”
class Cl3dHomebrew : public ClHomebrew
{
protected:
Cl3dHomebrew();
virtual ~Cl3dHomebrew();
};
Die Initialiserung wird ebenfalls in der init-Methode stattfinden die wir nun neu ausprägen.
class Cl3dHomwbrew : public ClHomebrew
{
public:
/*
* Die initialisierung der GU Homebrew.
*/
bool init();
/*
* Hier werden alle Aufräumarbeiten vor’m verlassen der
* Homebrew platziert.
*/
void exit();
Die Implementierung der Klasse und der Initialisierung greift dabei die Überlegungen auf dieser Seite auf. Darum werde ich den gesammten Quellcode nicht nochmals hier rein stellen, sondern nur Ausschnittsweise andeuten. Zusätzlich notwendige Includes sind die pspgu.h und die pspgum.h:
extern “C” {
#include <pspgu.h>
#include <pspgum.h>
}
#include “3dHomebrew.h”
static unsigned int __attribute__((aligned(16))) list[262144];
bool Cl3dHomebrew::init(){
//zuerst die Superklassen Methode rufen
if (!ClHomebrew::init()) return false;
//nun die eigene Initialisierung der GU
sceGuInit();
…….
//abschließen der GU Liste führt zur sofortigen Abarbeitung
//in diesem Falle, da wir die Liste mit GU_DIRECT gestartet haben
sceGuFinish();
sceGuSync(0,0);
//Aktiviere die Anzeige der GU auf dem realen Bildschirm
sceGuDisplay(GU_TRUE);
return true;
}
void Cl3dHomebrew::exit(){
//GU beenden
sceGuTerm();
//Superklassen Methode am Schluss
ClHomebrew::exit();
}
Nun ist der Punkt erreicht, an dem wir unsere erste GU Homebrew beginnen können. Um den Einstieg nun möglichst einfach zu gestalten, habe ich die bis hier beschriebenen Basisklassen und Vorbereitungen mal in einer Bibliothek zusammen gefasst. Zusammen mit den Header-Files könnt Ihr diese in Euer PSPSDK einbinden und schon loslegen, ohne die ganzen Quellcodes hier abschreiben zu müssen.
Die Bibliothek PspHbc bietet dabei die Basis-Homebrewklasse, eine 2D Grafik-Basisklasse, so wie sie bei der Voxellandschaft zum Einsatz kam, und die nun neue 3D-GU Basisklasse.
Wie Ihr diese Bibliothek für Eure eigenen Projekte einsetzt um eigene Homebrews zu schreiben zeige ich am Beispiel einer kleinen 3D Homebrew in Kürze…
Die Dokumentation der Header-Files in dem Paket sind auf English - aber da hier alles auf deutsch erklärt wurde, sollte das kein Problem darstellen.
Geschrieben in GU | Drucken | Keine Kommentare »
GU - und nu ?
12.1.2010 von AnMaBaGiMa.
Die GU - das ist die “Graphic Unit” der PSP. Ich habe keine wirkliche Auflösung dieses Kürzels gefunden, aber im Grunde kann man sich die GU wie einen Teil der Grafikkarte eines PC’s vorstellen. Dieser Teil ist in der Lage 3D Grafiken zu berechnen und darzustellen.
Der Vorteil der GU ist, dass diese eine sehr leistungsstarke Einheit ist, die quasi parallel zur CPU arbeitet und einen optimierten Datenbus zur Grafikausgabe und zu bestimmten Hauptspeicherbereichen hat. Wer sich schon mit verschieden 3D Spielen auf dem PC auseinander gesetzt hat weiß vielleicht, dass Hardwarebeschleunigung in einer Grafikkarte bei der Berechnung der Grafik gegenüber der reinen CPU enorme Vorteile und Performancegewinn bietet und uns dadurch auch auf der PSP vermutlich einige Türen öffnen wird.
Der “Haken” an der ganzen Sache ? Nun - wir müssen uns wieder durch ein wenig staubige Theorie quälen um zu verstehen, was wir machen müssen um mit der GU zu arbeiten und erste Resultate zu erzielen….
Damit dieser Teil der ersten Betrachtung der GU und weitere Erkenntnisse nicht in der Blog-Geschichte untergehen, platziere ich das ganze auf einer separaten Seite…
Geschrieben in GU | Drucken | 1 Kommentar »