<?xml version="1.0" encoding="UTF-8"?>
<!-- generator="wordpress/2.2.1" -->
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	>

<channel>
	<title>PSP Spieleentwicklung</title>
	<link>http://psp.anmabagima.de</link>
	<description></description>
	<pubDate>Wed, 26 Jan 2011 11:59:39 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.2.1</generator>
	<language>en</language>
			<item>
		<title>Der Blog zieht um</title>
		<link>http://psp.anmabagima.de/2011/01/26/der-blog-zieht-um/</link>
		<comments>http://psp.anmabagima.de/2011/01/26/der-blog-zieht-um/#comments</comments>
		<pubDate>Wed, 26 Jan 2011 11:59:39 +0000</pubDate>
		<dc:creator>AnMaBaGiMa</dc:creator>
		
		<category><![CDATA[Allgemein]]></category>

		<guid isPermaLink="false">http://psp.anmabagima.de/2011/01/26/der-blog-zieht-um/</guid>
		<description><![CDATA[Auch wenn auf diesem Blog schon eine ganze weile nix mehr los war&#8230;..Er zieht um&#8230;.
Die Form des Blog&#8217;s ist nun für die eigentliche Zielsetzung zu unübersichtlich geworden&#8230;darum zieht der Teil in die Projektecke unserer Homepage um: PSP-Homebrew-Projektecke.
Noch ist nicht der gesammte Kontekt transferiert&#8230;wenn dies abgeschlossen ist, wird dieser Blog hier &#8220;dicht&#8221; gemacht&#8230;.
]]></description>
			<content:encoded><![CDATA[<p>Auch wenn auf diesem Blog schon eine ganze weile nix mehr los war&#8230;..Er zieht um&#8230;.</p>
<p>Die Form des Blog&#8217;s ist nun für die eigentliche Zielsetzung zu unübersichtlich geworden&#8230;darum zieht der Teil in die Projektecke unserer Homepage um: <a href="http://www.anmabagima.de/PSP/psphomebrew.htm">PSP-Homebrew-Projektecke</a>.</p>
<p>Noch ist nicht der gesammte Kontekt transferiert&#8230;wenn dies abgeschlossen ist, wird dieser Blog hier &#8220;dicht&#8221; gemacht&#8230;.</p>
]]></content:encoded>
			<wfw:commentRss>http://psp.anmabagima.de/2011/01/26/der-blog-zieht-um/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Der Anstrich - aber mit Plan</title>
		<link>http://psp.anmabagima.de/2010/03/23/der-anstrich-aber-mit-plan/</link>
		<comments>http://psp.anmabagima.de/2010/03/23/der-anstrich-aber-mit-plan/#comments</comments>
		<pubDate>Tue, 23 Mar 2010 11:55:49 +0000</pubDate>
		<dc:creator>AnMaBaGiMa</dc:creator>
		
		<category><![CDATA[GU]]></category>

		<guid isPermaLink="false">http://psp.anmabagima.de/2010/03/23/der-anstrich-aber-mit-plan/</guid>
		<description><![CDATA[
                                                       [...]]]></description>
			<content:encoded><![CDATA[<link rel="stylesheet" type="text/css" href="http://www.anmabagima.de/blog/styles/shCore.css" />
<link rel="stylesheet" type="text/css" href="http://www.anmabagima.de/blog/styles/shThemeAnmabagima.css" /><script type="text/javascript" src="http://www.anmabagima.de/blog/scripts/shCore.js"></script><script type="text/javascript" src="http://www.anmabagima.de/blog/scripts/shBrushCpp.js"></script><script language="javascript">                                                              SyntaxHighlighter.config.bloggerMode = true;SyntaxHighlighter.config.clipboardSwf = "http://www.anmabagima.de/blog/scripts/clipboard.swf";SyntaxHighlighter.all();</script>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.
<p><strong>Texturen verwalten</strong></p>
<p>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 &#8220;leider&#8221; keine Verwaltungsfunktionen mit. Darum werden wir hier eine sehr einfache Variante in Form einer Texterverwaltungsklasse selber erstellen. Ein paar Grundüberlegungen:<br />
1. die Texturen sollen zunächst nur von PNG Bilddateien gelesen werden<br />
2. ein und das selbe Bild soll als Textur nur einmal geladen werden<br />
3. geladene Texturen sollen individuell wieder freigegeben werden können um Speicher zu sparen<br />
4. die Texturen sollen ge-swizzled werden<br />
5. die Klasse soll einfach erweiterbar sein, zBsp. um Laderoutinen für JPG oder andere Bildformate</p>
<p>Dies sind ein paar gute Rahmenbedingungen. Machen wir uns nun ans Werk. Wir erstellen eine neue Klasse mit dem Namen <em>ClTextureMgr</em>. Im Header definieren wir zunächst ein paar Beschränkungen, wie die maximale Größe einer Textur, die sich auf 512&#215;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.</p>
<pre class="brush:cpp">
/*
 * 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;</pre>
<p>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.</p>
<pre class="brush:cpp">
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_ */</pre>
<p>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 <img src='http://psp.anmabagima.de/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> ). Daneben sind die statischen Methoden für die Verwendung der Klasse als Singleton implementiert.</p>
<pre class="brush:cpp">
/*
 * TextureMgr.cpp
 */    

//die notwendigen Includes
extern "C"{
#include &lt;malloc.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;png.h&gt;
#include &lt;psptypes.h&gt;
#include &lt;pspgu.h&gt;
}    

#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 &gt;&gt;= 1;
	b = 1 &lt;&lt; n;
	if (b == 2 * width) b &gt;&gt;= 1;
	return b;
}    

//Am Anfang ist die Singleton instanz nicht erzeugt
ClTextureMgr* ClTextureMgr::_instance = 0;    

ClTextureMgr::ClTextureMgr() {
	//der Konstruktor initialisiert die Datan
	this-&gt;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);
	}
}</pre>
<p>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.</p>
<pre class="brush:cpp">
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&lt;managedTextures;t++){
		if (textures[t].name){
			if (strncmp(filename, textures[t].name, strlen(filename))==0){
				return &amp;textures[t];
			}
		}
	}
	//es ist wirklich eine neue Textur, also diese Laden und die Daten füllen
	managedTextures++;
	if (managedTextures &gt;= MAX_TEX_COUNT) return NULL;   

	// get the pointer of the new texture data structure
	Texture* newTex = &amp;textures[managedTextures-1];
	newTex-&gt;name = (char*)malloc(strlen(filename));
	strcpy(newTex-&gt;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-&gt;id = managedTextures;   

	newTex-&gt;swizzled = false;
	//veruche die Texturdaten zu swizzlen
	//Speicher für die neuen Daten besorgen
	void* swizzleData = malloc(sizeof(u32)*newTex-&gt;stride*newTex-&gt;height);
	if(swizzleData){
		swizzle((unsigned char*)swizzleData, (unsigned char*)newTex-&gt;data, newTex-&gt;stride*sizeof(u32), newTex-&gt;height);
		newTex-&gt;swizzled = true;
		//Speicher der originalen Daten freigeben
		free(newTex-&gt;data);
		newTex-&gt;data = swizzleData;
	} else
		newTex-&gt;swizzled = false;   

	return newTex;
}</pre>
<p><strong>Swizzle</strong></p>
<p>Das nun schon oft erwähnte Wort bedeutet direkt übersetzt: Bauernfängerei <img src='http://psp.anmabagima.de/wp-includes/images/smilies/icon_biggrin.gif' alt=':D' class='wp-smiley' /> bzw. mit dem Zusatz &#8220;Stick&#8221; 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 &#8220;durcheinander&#8221; 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.<br />
Eine leicht lesbare - nicht auf Performance getrimmte - Swizzle-Routine wird in unserer Textur-Verwaltungsklasse verwendet:</p>
<pre class="brush:cpp">
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 &lt; height; ++j)
   {
      for (i = 0; i &lt; 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];
      }
   }
}</pre>
<p>Abschließend und der Vollständigkeit halber die Methode um eine verwaltete Textur einer bestimmten ID zurückzuliefern, eine Textur freizugeben und den Klassendestruktor.</p>
<pre class="brush:cpp">
Texture *ClTextureMgr::getTexture(unsigned int id){
	//Prüfen das die ID auch valide ist
	if (id &gt; managedTextures){
		return NULL;
	}  

	return &amp;textures[id-1];
}  

void ClTextureMgr::freeTexture(unsigned int id){
	Texture* text = &amp;textures[id-1];
	if (text){
		free(text-&gt;data);
		free(text-&gt;name);
		text-&gt;data = 0;
		text-&gt;name = 0;
	}
}  

ClTextureMgr::~ClTextureMgr() {
	//Allen reservierten Speicher freigeben
	for (int t=0;t&lt;managedTextures;t++){
		if (textures[t].data) free(textures[t].data);
		if (textures[t].name) free(textures[t].name);
	}
	if (managedTextures)
		free(textures);
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://psp.anmabagima.de/2010/03/23/der-anstrich-aber-mit-plan/feed/</wfw:commentRss>
		</item>
		<item>
		<title>3D Landschaft - optimiert</title>
		<link>http://psp.anmabagima.de/2010/03/04/3d-landschaft-optimiert/</link>
		<comments>http://psp.anmabagima.de/2010/03/04/3d-landschaft-optimiert/#comments</comments>
		<pubDate>Thu, 04 Mar 2010 12:48:54 +0000</pubDate>
		<dc:creator>AnMaBaGiMa</dc:creator>
		
		<category><![CDATA[Allgemein]]></category>

		<guid isPermaLink="false">http://psp.anmabagima.de/2010/03/04/3d-landschaft-optimiert/</guid>
		<description><![CDATA[Unsere erste 3D Landschaft bestehend aus vielen kleinen Dreiecken ist nun fertig. Bei genauerer Betrachtung der Daten die wir für die Landschaft benötigen - also der Anzahl der Dreiecke die entstehen werden, wir schnell an Grenzen stoßen wenn wir größere Landschaftsflächen abbilden wollen. Eine dieser Grenzen ist die Anzahl der Dreiecke die im Hauptspeicher der [...]]]></description>
			<content:encoded><![CDATA[<p>Unsere erste 3D Landschaft bestehend aus vielen kleinen Dreiecken ist nun fertig. Bei genauerer Betrachtung der Daten die wir für die Landschaft benötigen - also der Anzahl der Dreiecke die entstehen werden, wir schnell an Grenzen stoßen wenn wir größere Landschaftsflächen abbilden wollen. Eine dieser Grenzen ist die Anzahl der Dreiecke die im Hauptspeicher der PSP abgelegt werden können und zum anderen natürtliche die Anzahl der Dreiecke die in akzeptabler Geschwindigkeit von der GU prozessiert und gezeichnet werden können. Bevor wir unserer Landschaft &#8220;Leben&#8221; in Form von Bewegung einhauchen werden wir einen kurzen Exkurs in die Optimierung der Landschaft unternehmen.</p>
<p>Schlagworte die hier oft in diesem Zusammenhang aufgeführt werden sind SOAR oder LOD. Was dies ist und welche Methodik zur Optimierung ich gefunden habe (im Internet) stelle ich in diesem <a href="http://psp.anmabagima.de/digitale-landschaften-in-3d/drahtgitterlandschaft/" title="Landschaftsoptimierung">Bereich</a> vor. Neben der theoretischen Abhandlung werde ich dort auch meine Ideen für die PSP Implementierung bereitstellen (zur Zeit noch in Arbeit).</p>
<p>Unabhängig davon können aber die sich an diese Überlegungen anschließenden nächsten Schritte in unserer Landschaft (Textur, Bewegung etc.) umgesetzt werden.</p>
]]></content:encoded>
			<wfw:commentRss>http://psp.anmabagima.de/2010/03/04/3d-landschaft-optimiert/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Landschaft 3D mit GU</title>
		<link>http://psp.anmabagima.de/2010/02/17/landschaft-3d-mit-gu/</link>
		<comments>http://psp.anmabagima.de/2010/02/17/landschaft-3d-mit-gu/#comments</comments>
		<pubDate>Wed, 17 Feb 2010 11:04:06 +0000</pubDate>
		<dc:creator>AnMaBaGiMa</dc:creator>
		
		<category><![CDATA[Komplexe 3D Welten]]></category>

		<guid isPermaLink="false">http://psp.anmabagima.de/2010/02/17/landschaft-3d-mit-gu/</guid>
		<description><![CDATA[
                                                       [...]]]></description>
			<content:encoded><![CDATA[<link rel="stylesheet" type="text/css" href="http://www.anmabagima.de/blog/styles/shCore.css" />
<link rel="stylesheet" type="text/css" href="http://www.anmabagima.de/blog/styles/shThemeAnmabagima.css" /><script type="text/javascript" src="http://www.anmabagima.de/blog/scripts/shCore.js"></script><script type="text/javascript" src="http://www.anmabagima.de/blog/scripts/shBrushCpp.js"></script><script language="javascript">                                                          SyntaxHighlighter.config.bloggerMode = true;SyntaxHighlighter.config.clipboardSwf = "http://www.anmabagima.de/blog/scripts/clipboard.swf";SyntaxHighlighter.all();</script>Nun haben wir unser erstes Objekt im virtuellen Raum platziert. Nun können wir mit diesem Objekt einige Experimente machen um diverse Funktionen der GU an genau diesem auszuprobieren. Dies möchte ich hier aber etwas nach hinten schieben und allen Entdeckern überlassen. Denn unsere nächste Etappe soll sich mit der Landschaft die wir zunächst im Voxelspace betrachten konnten beschäftigen. Wie können wir diese über die GU im 3D Raum modellieren und betrachten ? Diese Frage wollen wir nun beantworten und an diesem reellen Beispiel nach und nach die einzelnen Möglichkeiten der GU ausprobieren und hoffentlich sinnvoll einzusetzen lernen.
<p>Als Vorbereitung für diesen Teil legen wir zunächst ein neues Projekt mit dem Namen  <em>GuLandscape</em> in Eclipse an. Die ersten Schritte sind nun ein Makefile in diesem Projekt zu erstellen, eine Applikationsklasse zu definieren und die main.cpp zu erstellen. Doch nochmal langsam der Reihe nach.</p>
<p><strong>1. Das Makefile:</strong></p>
<pre class="brush:cpp">
TARGET = GuLandscape
OBJS = main.o GuLandscapeApp.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 = Gu Landscape
PSP_LARGE_MEM=1
PSP_FW_VERSION=500
PSPSDK=$(shell psp-config --pspsdk-path)
include $(PSPSDK)/lib/build.mak
</pre>
<p><strong>2. Die neue Applikationsklasse ClGuLandscapeApp:</strong></p>
<pre class="brush:cpp">
*
* GuLandscapeApp.h
* Author: andreborrmann
*/

#ifndef GULANDSCAPEAPP_H_
#define GULANDSCAPEAPP_H_
#include &lt;3dHomebrew.h&gt;
class ClGuLandscapeApp : public Cl3dHomebrew {
public:
/*
* die Methoden zum erzeigen und aufr&#228;umen der Singleton Instanz der
* Applikationsklasse
*/
static ClGuLandscapeApp* getInstance();
static void releaseInstance();
/*
* &#220;berladen der Initi-Routine
*/
bool init();
protected:
ClGuLandscapeApp();
virtual ~ClGuLandscapeApp();
/*
* &#220;berladen der Render Methode
*/
void render();
static ClGuLandscapeApp* _instance;
};
#endif /* GULANDSCAPEAPP_H_ */
</pre>
<pre class="brush:cpp">
/*
* GuLandscapeApp.cpp
*/
extern “C”{
#include &lt;pspgu.h&gt;
}
#include “GuLandscapeApp.h”
ClGuLandscapeApp* ClGuLandscapeApp::_instance = 0;
ClGuLandscapeApp* ClGuLandscapeApp::getInstance(){
 if (!_instance){
  _instance = new ClGuLandscapeApp();
 }
 return _instance;
}
void ClGuLandscapeApp::releaseInstance(){
 if (_instance){
  delete(_instance);
  _instance = 0;
 }
}
bool ClGuLandscapeApp::init(){
//zuerst die Super-Methode
if (!Cl3dHomebrew::init()) return false;
//hier nun die eigene Initialisierung…
return true;
}
void ClGuLandscapeApp::render() {
}
ClGuLandscapeApp::ClGuLandscapeApp() {
}
ClGuLandscapeApp::~ClGuLandscapeApp() {
}
</pre>
<p><strong>3. Die main.cpp</strong></p>
<pre class="brush:cpp">
/*
* main.cpp
*/
extern “C”{
#include &lt;pspkernel.h&gt;
}
#include “GuLandscapeApp.h”
PSP_MODULE_INFO(”GU Landscape”, 0, 1, 1);
PSP_MAIN_THREAD_ATTR(PSP_THREAD_ATTR_USER);
PSP_HEAP_SIZE_KB(-1024);
int main(int argc, char* argv[])
{
ClGuLandscapeApp* myHomebrew = ClGuLandscapeApp::getInstance();
if (myHomebrew-&gt;init()){
myHomebrew-&gt;run();
myHomebrew-&gt;exit();
}
ClGuLandscapeApp::releaseInstance();
sceKernelExitGame();
return 0;
}
</pre>
<p>Das war nun das Rahmenwerk für unsere neue Homebrew. Nun schauen wir uns den Teil an indem wir aus einer in einer Höhenkarte abgelegten Landschaft eine 3 dimensionale virtuelle Welt erschaffen. Ähnlich wie beim Ansatz im Voxelspace gehen wir dabei davon aus dass die X und Y Werte des Bildes die Ausdehnung der Landschaft in Breite und Tiefe  und der Farbwert die Höhe beschreiben. Um unsere neue Homebrew möglichst übersichlich zu halten, werden wir die Umwandlung des 3D Terrains aus einer Höhenkarte und die Darstellung desselben in einer eigenen Klasse kapseln. Dadurch bleibt die Applikationsklasse klein und erweiterbar für weitere Funktionen die wir hier nach und nach - quasi Modular - Hinzufügen können.</p>
<p><strong>Das Terrain</strong></p>
<p>Für die Abbildung des Terrains legen wir eine  neue Klasse mit dem Namen ClGuTerrain an. Diese Klasse wird nicht von einer anderen Abgeleitet. Die Klasse soll folgendermaßen aufgebaut sein:<br />
Im Konstruktor übergeben wir den Dateinamen (.png) der Höhenkarte die maximal 512&#215;512 Bildpunkte groß sein darf. Hier werden die einzelnen Bildpunkte aus der Datei extrahiert und in einem Array mit ihren Eigenschaften abgelegt.<br />
In einem zweiten Schritt werden aus diesen Punkten die von der GU verarbeitbaren Vertices erstellt und zu einer Vertexliste zusammengesetzt. Dabei werden wir nicht wie in unserem ersten Beispiel ein enzelnes Dreieck erstellen, sondern die Landschaft in viele viele Dreiecke aufteilen. Dabei wird ein (quadratischer) Bereich der virtuellen Landschaft der aus 4 benachbarten Punkten besteht durch genau 2 Dreiecke dargestellt - im Detail zeigen das die Bilder die etwas weiter unten zu sehen sind.</p>
<p>Die Definition der neuen Klasse:</p>
<pre class="brush:cpp">
/*
* GuTerrain.h Die Klasse kapselt die Beschreibung und Darstellung eines 3D Terrain
* basierend auf einer Hightmap PNG Datei
*/
#ifndef GUTERRAIN_H_
#define GUTERRAIN_H_
/*
* Struktur zur Definition eines Punktes im Terrain
*/
typedef struct TerrainPoint{
int x, y; // Koordinaten
int height; // H&#246;he des Punktes
int color; // Farbe des Punktes
}TerrainPoint;
/*
* Struktur zur Definition eines GU Vertexes
*/
typedef struct Vertex {
int color;
float x, y, z;
}Vertex;
class ClGuTerrain {
public:
/*
* Der Konstruktor erh&#228;lt den Filenamen der H&#246;henkarte
* aud dessen Basis das 3D Terrain erzeugt
*/
ClGuTerrain(const char* heightMapFile);
virtual ~ClGuTerrain();
/*
* Render Methode f&#252;r das Terrain
*/
void render();

protected:
int width, height; //Gr&#246;&#223;e des Terrains
TerrainPoint* terrainPoints;
Vertex* mesh; //Das Vertex-Netz des Terrains
int vertexCount;
/*
* Generierung der GU Vertices f&#252;r das Terrain
*/
void generateMesh();

/*
* Laderoutine die ein PNG l&#228;dt und die Bilddaten zur&#252;ckliefert
* der ben&#246;tigte speicher wird in der Methode reserviert
*/
bool loadPNG(const char* fileName, void** data);
/*
* Diverse Daten sollten der Einfachheit halber immer in gr&#246;&#223;en einer Potenz von 2 abgelegt
* sein. Diese Methode liefert von einer Zahl die n&#228;chstgr&#246;&#223;ere potenz von 2 - also 2 hoch n
*/
int getPowerOf2(int value);
};
#endif /* GUTERRAIN_H_ */
</pre>
<p>Die Implementierung dieser Klasse zeige ich nun Schritt für Schritt. Die Methode zum laden des PNG Files bedarf nun keiner großen Eklärungen mehr, da wir das bereits bei der Voxellandscape benutzt haben. Der Konstruktor ruft zunächst diese Methode auf um das Bild zu laden und extrahiert die Bilddaten dann in die Liste der Terrain Punkte.</p>
<pre class="brush:cpp">
/*
* Die notwendigen Header Files
*/
extern “C”{
#include &lt;stdio.h&gt;
#include &lt;png.h&gt;
#include &lt;malloc.h&gt;
#include &lt;psptypes.h&gt;
#include &lt;pspgum.h&gt;
#include &lt;pspgu.h&gt;
}
#include “GuTerrain.h”
ClGuTerrain::ClGuTerrain(const char *heightMapFile){
//Laden des H&#246;henkartenbildes und die Punkte extrahieren
 int* altitude;
terrainPoints = 0; if (loadPNG(heightMapFile, (void**)&amp;altitude)){
// Nun erzeugen wir die Punktinformationen welche die Landschaft aufspannen
  // diese werden sp&#228;ter in ein GU 3d “Drahtgitternetz” (mesh) umgewandelt.
  this-&gt;terrainPoints = (TerrainPoint*)malloc(width*height*sizeof(TerrainPoint));
  for (int x=0;x&lt;width;x++){
   for (int y=0;y&lt;height;y++){
    terrainPoints[x+width*y].x = -width/2 + x; //das Terrain soll im virtuellen Raum zentral Platziert sein
    terrainPoints[x+width*y].y = -height/2 + y;
    terrainPoints[x+width*y].height = 255 &amp; altitude[x+width*y];
    terrainPoints[x+width*y].color = altitude[x+width*y];
   }
  }
// den Speicher des Bildes freigeben, da er nun nicht mehr gebraucht wird.
  free(altitude);
// nun die Punkte in GU Vertices konvertieren
generateMesh();
 }
}
</pre>
<p>Der nächste Schritt wird nun die Generierung der sogenannten Mesh werden, also des &#8220;Drahtgitter&#8221; Modells wie man es vielleicht aus 3D Modellierungsprogrammen kennt. Im Grunde besteht dieses Drahtgitter wie schon erwähnt aus Dreiecken. Nun wird jedes Dreieck durch 3 Punkte definiert. Das bedeutet, dass wir bei einer Höhenkarte von 4&#215;4 Punkten bereits 3&#215;3 Quadrate von jeweils 2 Dreiecken mit jeweils 3 Punkten, also 18&#215;3 - 54 Punkte für die Darstellung brauchen. Es gibt hier eine Optimierungsmöglichkeit die uns sowohl Speicherplatz beim Erzeugen der Mesh spart als auch Performancevorteile bringt. Es kommt ein sogenannter  Dreiecksstreifen (TriangleStripe) zum Einsatz. Dabei werden die Punkte so angeordnet, dass die ersten 3 Punkte ein vollständiges Dreieck definieren. Für jedes weitere Dreieck wird dann nur noch ein neuer Punkt gebraucht der dann mit den beiden zuletzt aufgeführten Punkten wieder ein Dreieck bildet.<br />
Eine kleine Illustration:<img align="middle" width="206" src="http://www.anmabagima.de/blog/images/triangle_strips.png" alt="Triangle Strip" height="167" style="width: 206px; height: 167px" title="Triangle Strip" />Es ist schön zu sehen dass für die 3 Dreiecke T1 bis T3 nun anstelle von 9 Punkten nur 5 benötigt werden. Um einen fortlaufenden Dreiecksstreifen zu erhalten müssen wir beim Wechsel in eine neue Zeile &#8220;unsichtbare&#8221; Dreiecke hinzufügen. Das ist in dem folgenden Bild mit den blauen Linien veranschaulicht. Die Zahlen repräsentieren die Vertexpunkte.<br />
<img width="187" src="http://www.anmabagima.de/blog/images/triangle_strip2.png" alt="Triangle Strip Hidden Triangles" height="174" style="width: 187px; height: 174px" title="Triangle Strip Hidden Triangles" /><br />
Dieses Verfahren werden wir nun bei der Erstellung der Vertexliste (Mesh) anwenden, auch wenn dieses offensichtlich noch einiges Optimierungspotential hat (da Punkte mehrfach/doppelt erzeugt werden):</p>
<pre class="brush:cpp">
void ClGuTerrain::generateMesh() {
// Erzeugen der Vertices f&#252;r ein TriangleStripe
 // Dies wird von links nach rechts aufgebaut Zeile f&#252;r Zeile
 // Das bedeutet, dass wir jeweils am Ende ein ganz ganz flaches (unsichtbares) Dreieck
// platzieren um den Wechsel in eine neue Zeile ohne Fehler zu erm&#246;glichen.
// zuerst den Speicherbedarf f&#252;r die Vertexe bestimmen und allokieren

 mesh=0;
 this-&gt;vertexCount = 2*(width+2)*height;
 mesh = (Vertex*)malloc(this-&gt;vertexCount*sizeof(Vertex));
 int vertex = 0;
 float scale = 0.5f;
 for (int y=0;y&lt; height-1; y++){
  for (int x=0;x &lt; width; x++){
// Die Vertices werden in der Zeile immer Paarweise erzeugt.
   mesh[vertex].x = terrainPoints[x+y*width].x*scale;
   mesh[vertex].z = terrainPoints[x+y*width].y*scale;
   mesh[vertex].y = (terrainPoints[x+y*width].height / 10.0f)- 20.0f;
   mesh[vertex].color = terrainPoints[x+y*width].color;
   vertex++;
  mesh[vertex].x = terrainPoints[x+(y+1)*width].x*scale;
   mesh[vertex].z = terrainPoints[x+(y+1)*width].y*scale;
   mesh[vertex].y = (terrainPoints[x+(y+1)*width].height / 10.0f)- 20.0f;
   mesh[vertex].color = terrainPoints[x+(y+1)*width].color;
   vertex++;
// sobald wir am rechten Rand angekommen sind m&#252;ssen wir das “unsichtbare”
   // Dreieck erzeugt. Dazu wird der letzte Vertex und der 2. der ersten Zeile nochmal angeh&#228;ngt
// das ganze soll aber nicht in der letzten Zeile passieren…

   if (x == (width-1) &amp;&amp; y &lt; (height-2)){
    mesh[vertex].x = mesh[vertex-1].x;
    mesh[vertex].y = mesh[vertex-1].y;
    mesh[vertex].z = mesh[vertex-1].z;
    mesh[vertex].color = mesh[vertex-1].color;
    vertex++;
    mesh[vertex].x = terrainPoints[(y+1)*width].x*scale;
    mesh[vertex].z = terrainPoints[(y+1)*width].y*scale;
    mesh[vertex].y = (terrainPoints[(y+1)*width].height / 10.0f)- 20.0f;
    mesh[vertex].color = terrainPoints[(y+1)*width].color;
    vertex++;
   }
  }
 }
// Damit wir wissen wie viele Punkte unser Mesh wirklich hat merken wir uns hier die Anzahl
 this-&gt;vertexCount = vertex;
}
</pre>
<p>Der nun noch offene Teil befasst sich mit der Darstellung des Terrain. Dazu wird die render-Methode ausgeprägt. Die Darstellung der Dreiecke erfolgt mit der nun schon bekannten Funktion <em>sceGumDrawArray</em>. Dabei ist zu beachten, dass diese nur maximal 65535 Vertices verarbeiten kann. Darum müssen wir unser gesammtes Drahtgitter - sollte es aus mehr Punkten bestehen - in mehreren Häppchen übergeben. Der Einfachheithalber wählen wir hier Pakete von maximal 65000 Punkten.</p>
<pre class="brush:cpp">
void ClGuTerrain::render(){
 // Rendern des Terrains. Dabei nur bis zu 65k Vertices darstellen
 // andernfalls kann es zu Abst&#252;rzen kommen, da die GU nur bis zu 65535 Vertices
// auf einmal rendern kann

 int renderVertices;
 sceGumMatrixMode(GU_VIEW);
 sceGumLoadIdentity();
 sceGumMatrixMode(GU_MODEL);
 sceGumLoadIdentity();

 sceGuFrontFace(GU_CCW);

 renderVertices = vertexCount;
 Vertex* drawMesh = mesh;
 while (renderVertices&gt;65000){
  sceGumDrawArray(GU_TRIANGLE_STRIP, GU_COLOR_8888 | GU_VERTEX_32BITF | GU_TRANSFORM_3D, 65000, 0, drawMesh);
  renderVertices-=65000;
  drawMesh+=65000;
 }

 if (renderVertices &gt; 0){
  sceGumDrawArray(GU_TRIANGLE_STRIP, GU_COLOR_8888 | GU_VERTEX_32BITF | GU_TRANSFORM_3D, renderVertices, 0, drawMesh);
 }
}
</pre>
<p>Abschließend müssen noch der Destruktor und die Hilfsmethode mit &#8220;Leben&#8221; gefüllt werden. Da diese keiner weiteren Erklärung bedürfen (glaube ich) hier das Coding:</p>
<pre class="brush:cpp">
int ClGuTerrain::getPowerOf2(int value){
short c;
int temp;

temp = value;
c=0;
while ((temp &gt;&gt;= 1) &gt; 0) c++;
temp = 1 &lt;&lt; c+1;

if (temp &gt;&gt; 1 == value){
temp = value;
}

return temp;
}

ClGuTerrain::~ClGuTerrain() {

if (terrainPoints)
free(terrainPoints);
if (mesh)
free(mesh);
}
</pre>
<p>Die nächsten Schritte werden nun das Terrain in unserer Landscape Homebrew zur Verfügung stellen, es erzeugen und rendern. Dazu sind nur noch kleine Anpassungen notwendig. Zuerst muss das Makefile erweitert werden, so dass die Terrain-Klassen-Objektdatei erzeugt wird:</p>
<pre class="brush:cpp">
OBJS = main.o GuLandscapeApp.o GuTerrain.o
</pre>
<p>In der LandscapeApp-Klasse fügen wir in der Header Datei die neue Klassenheaderdatei hinzu und definieren ein Attribut das eine Referenz auf die Terrainklasse enthalten kann:</p>
<pre class="brush:cpp">
#include “GuTerrain.h”

class ClGuLandscapeApp : public Cl3dHomebrew {
 ……

protected:

ClGuTerrain* terrain;
</pre>
<p>Die Implementierung der Applikationsklasse wird nun noch in der Init, der Render und Exit Methode angepasst:</p>
<pre class="brush:cpp">
bool ClGuLandscapeApp::init(){
if (!Cl3dHomebrew::init()) return false;

terrain = new ClGuTerrain(”altitude.png”);
if (!terrain) return false;
}

void ClGuLandscapeApp::render() {
if (terrain) terrain-&gt;render();
}

void ClGuLandscapeApp::exit(){
if (terrain)
delete(terrain);
}
</pre>
<p><strong>Das Ergebnis</strong></p>
<p>Wenn alles glatt geht, dann sollte das Ergebnis auf dem PSP Screen so aus sehen:<br />
<img align="middle" width="480" src="http://www.anmabagima.de/blog/images/GuLandscape.png" height="272" style="width: 480px; height: 272px" /></p>
]]></content:encoded>
			<wfw:commentRss>http://psp.anmabagima.de/2010/02/17/landschaft-3d-mit-gu/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Ein Dreieck - GU Test</title>
		<link>http://psp.anmabagima.de/2010/01/28/ein-dreieck-gu-test/</link>
		<comments>http://psp.anmabagima.de/2010/01/28/ein-dreieck-gu-test/#comments</comments>
		<pubDate>Thu, 28 Jan 2010 17:06:06 +0000</pubDate>
		<dc:creator>AnMaBaGiMa</dc:creator>
		
		<category><![CDATA[GU]]></category>

		<guid isPermaLink="false">http://psp.anmabagima.de/2010/01/28/ein-dreieck-gu-test/</guid>
		<description><![CDATA[
                                                       [...]]]></description>
			<content:encoded><![CDATA[<link rel="stylesheet" type="text/css" href="http://www.anmabagima.de/blog/styles/shCore.css" />
<link rel="stylesheet" type="text/css" href="http://www.anmabagima.de/blog/styles/shThemeAnmabagima.css" /><script type="text/javascript" src="http://www.anmabagima.de/blog/scripts/shCore.js"></script><script type="text/javascript" src="http://www.anmabagima.de/blog/scripts/shBrushCpp.js"></script><script language="javascript">                                                          SyntaxHighlighter.config.bloggerMode = true;SyntaxHighlighter.config.clipboardSwf = "http://www.anmabagima.de/blog/scripts/clipboard.swf";SyntaxHighlighter.all();</script>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.
<p>Die in der <a rel="attachment wp-att-23" href="http://psp.anmabagima.de/2010/01/20/homebrew-3d/psphbc/" title="PspHbc">PspHbc</a> Bibliothek enthaltenen Header Files entpackt ihr am besten in euer PSPSDK Verzeichnis <em>C:\pspsdk\psp\sdk\include</em> das Lib-File kommt dann in das Verzeichnis <em>C:\pspsdk\psp\sdk\lib</em> dadurch wird die Nutzung in eigenen Projekten stark vereinfacht.</p>
<p>Nun aber los. Wie schon bei unserer ersten &#8220;normalen&#8221; Homebrew beginnt alles mit dem erstellen einer neuen Klasse. Diese wird nun von der 3DHomebrew-Basisklasse abgeleitet. Unsere Beispiel Homebrew soll den Namen <strong>HbcGuSample</strong> tragen. Die Klasse heißt dann treffender Weise: <em>ClHbcGuSampleApp</em></p>
<pre class="brush:cpp">
/*
 * HbcGuSampleApp.h
 * Beispiel Implementierung der GU Homebrew Basisklasse
 */
#ifndef HBCGUSAMPLEAPP_H_
#define HBCGUSAMPLEAPP_H_
#include &lt;3dHomebrew.h&gt;
class ClHbcGuSampleApp : public Cl3dHomebrew {
public:
static ClHbcGuSampleApp* getInstance();
static void releaseInstance();
// Redefiniren der Render Methode um sie mit eigender Logik zu f&#252;llen
void render();protected: ClHbcGuSampleApp();
virtual ~ClHbcGuSampleApp();
static ClHbcGuSampleApp* _instance;
};
#endif /* HBCGUSAMPLEAPP_H_ */
</pre>
<p>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.</p>
<pre class="brush:cpp">
/*
* HbcGuSampleApp.cpp
*
*/
extern “C”{
#include&lt;pspgu.h&gt;
#include&lt;pspgum.h&gt;
}
#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;
}
}
</pre>
<p>Der für die Klasse generierte Constructor und Destructor bleiben unberührt bzw. leer. Der nun &#8220;spannende&#8221; Teil befindet sich in der <strong>render</strong> Methode. Hier wollen wir unser erstes Objekt im virtuellen Raum definieren.</p>
<p><em><strong>Wichtig:</strong>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.</em> <img src='http://psp.anmabagima.de/wp-includes/images/smilies/icon_biggrin.gif' alt=':D' class='wp-smiley' /> </p>
<p><strong>Das Dreieck</strong></p>
<p>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.</p>
<pre class="brush:cpp">
void ClHbcGuSampleApp::render(){
// Definition einer Struktur die einen 3D-Punkt beschreibt
typedef struct Vertex {
int color;
float x, y, z;
}Vertex;
//Speicher f&#252;r 3 Punkte reservieren
//dieser Speicher ist dabei nur tempor&#228;r und nur innerhalb der aktuellen GU Liste g&#252;ltig
Vertex* triangle = (Vertex*)sceGuGetMemory(sizeof(Vertex)*3);
//Daten f&#252;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;
</pre>
<p>Als nächsten Schritt wollen wir dieses Dreieck auch zeichnen. Zuerst leeren wir den Bildschirm mit einer von schwarz verschiedenen Farbe (schwarz macht&#8217;s die Basisklasse). Dadurch kann mann schneller erkennen ob hier was &#8220;gezeichnet&#8221; wird oder nicht, da ggfl. schwarz auf schwarz gezeichnete Objekte nicht sichtbar wären&#8230;<br />
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.</p>
<p>Doch nun zum Code:</p>
<pre class="brush:cpp">
//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&#252;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&#228;chlich auf den Bildschirm gebracht, oder
// besser in den Zeichenpuffer der beim n&#228;chsten wechsel zwischen Display- und Zeichenpuffer
// sichtbar wird
sceGumDrawArray(GU_TRIANGLES, GU_TRANSFORM_3D | GU_VERTEX_32BITF | GU_COLOR_8888, 3, 0, triangle);
}
</pre>
<p>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:</p>
<pre class="brush:cpp">
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
</pre>
<p>Wenn nun die Erstellung dieses Beispiels glatt gelaufen ist, dann sollte das ganze auf der PSP so aussehen:<br />
<img align="middle" width="480" src="http://www.anmabagima.de/blog/images/GuDreieck.png" alt="GU Dreick" height="272" style="width: 480px; height: 272px" title="GU Dreick" /></p>
]]></content:encoded>
			<wfw:commentRss>http://psp.anmabagima.de/2010/01/28/ein-dreieck-gu-test/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Homebrew - 3D</title>
		<link>http://psp.anmabagima.de/2010/01/20/homebrew-3d/</link>
		<comments>http://psp.anmabagima.de/2010/01/20/homebrew-3d/#comments</comments>
		<pubDate>Wed, 20 Jan 2010 11:27:10 +0000</pubDate>
		<dc:creator>AnMaBaGiMa</dc:creator>
		
		<category><![CDATA[GU]]></category>

		<guid isPermaLink="false">http://psp.anmabagima.de/2010/01/20/homebrew-3d/</guid>
		<description><![CDATA[
                                                       [...]]]></description>
			<content:encoded><![CDATA[<link rel="stylesheet" type="text/css" href="http://www.anmabagima.de/blog/styles/shCore.css" />
<link rel="stylesheet" type="text/css" href="http://www.anmabagima.de/blog/styles/shThemeAnmabagima.css" /><script type="text/javascript" src="http://www.anmabagima.de/blog/scripts/shCore.js"></script><script type="text/javascript" src="http://www.anmabagima.de/blog/scripts/shBrushCpp.js"></script><script language="javascript">                                                          SyntaxHighlighter.config.bloggerMode = true;SyntaxHighlighter.config.clipboardSwf = "http://www.anmabagima.de/blog/scripts/clipboard.swf";SyntaxHighlighter.all();</script>Die ersten Grundlagen für die GU habe ich zusammengestellt und die <a href="http://psp.anmabagima.de/?page_id=19" title="GU Initialisierung">Initialisierung</a> 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.
<p>Da die Grundlegende Homebrew Einrichtung (mit den Callbacks etc) bereits in einer Klasse zusammengefasst ist, werden wir diese einfach ableiten und eine neue Klasse <em>Cl3dHomebrew</em> erstellen:</p>
<pre class="brush:cpp">
#include “Homebrew.h”
class Cl3dHomebrew : public ClHomebrew
{
protected:
Cl3dHomebrew();
virtual ~Cl3dHomebrew();
};
</pre>
<p>Die Initialiserung wird ebenfalls in der init-Methode stattfinden die wir nun neu ausprägen.</p>
<pre class="brush:cpp">
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();
</pre>
<p>Die Implementierung der Klasse und der Initialisierung greift dabei die Überlegungen auf dieser <a href="http://psp.anmabagima.de/?page_id=19" title="Initialisieren der GU">Seite</a> auf. Darum werde ich den gesammten Quellcode nicht nochmals hier rein stellen, sondern nur Ausschnittsweise andeuten. Zusätzlich notwendige Includes sind die <em>pspgu.h</em> und die <em>pspgum.h</em>:</p>
<pre class="brush:cpp">
extern “C” {
#include &lt;pspgu.h&gt;
#include &lt;pspgum.h&gt;
}
#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&#223;en der GU Liste f&#252;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();
}
</pre>
<p>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.</p>
<p>Die Bibliothek <a rel="attachment wp-att-23" href="http://psp.anmabagima.de/2010/01/20/homebrew-3d/psphbc/" title="PspHbc">PspHbc</a> 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.</p>
<p>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&#8230;<br />
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.</p>
]]></content:encoded>
			<wfw:commentRss>http://psp.anmabagima.de/2010/01/20/homebrew-3d/feed/</wfw:commentRss>
		</item>
		<item>
		<title>GU - und nu ?</title>
		<link>http://psp.anmabagima.de/2010/01/12/gu-und-nu/</link>
		<comments>http://psp.anmabagima.de/2010/01/12/gu-und-nu/#comments</comments>
		<pubDate>Tue, 12 Jan 2010 08:15:29 +0000</pubDate>
		<dc:creator>AnMaBaGiMa</dc:creator>
		
		<category><![CDATA[GU]]></category>

		<guid isPermaLink="false">http://psp.anmabagima.de/2010/01/12/gu-und-nu/</guid>
		<description><![CDATA[Die GU - das ist die &#8220;Graphic Unit&#8221; 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&#8217;s vorstellen. Dieser Teil ist in der Lage 3D Grafiken zu berechnen und darzustellen.
Der Vorteil der GU ist, dass diese eine sehr leistungsstarke [...]]]></description>
			<content:encoded><![CDATA[<p>Die GU - das ist die &#8220;Graphic Unit&#8221; 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&#8217;s vorstellen. Dieser Teil ist in der Lage 3D Grafiken zu berechnen und darzustellen.</p>
<p>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.</p>
<p>Der &#8220;Haken&#8221; 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&#8230;.</p>
<p>Damit dieser Teil der ersten Betrachtung der GU und weitere Erkenntnisse nicht in der Blog-Geschichte untergehen, platziere ich das ganze auf einer separaten <a href="http://psp.anmabagima.de/?page_id=18">Seite&#8230;</a></p>
]]></content:encoded>
			<wfw:commentRss>http://psp.anmabagima.de/2010/01/12/gu-und-nu/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Neustart&#8230;</title>
		<link>http://psp.anmabagima.de/2010/01/11/neustart/</link>
		<comments>http://psp.anmabagima.de/2010/01/11/neustart/#comments</comments>
		<pubDate>Mon, 11 Jan 2010 10:18:52 +0000</pubDate>
		<dc:creator>AnMaBaGiMa</dc:creator>
		
		<category><![CDATA[Allgemein]]></category>

		<guid isPermaLink="false">http://psp.anmabagima.de/2010/01/11/neustart/</guid>
		<description><![CDATA[Nun bin ich schon so weit gekommen. Die Ergebnisse sind auch schon ganz passabel, aber können doch mit dem aktuellen Anspruch an 3D Spiele nicht mithalten. Die Berechnung der Voxellandschaft ist sehr CPU lastig und kann die vielen Vorteile der in der PSP verbauten Grafikhardware nicht nutzen.
Ernüchternd macht sich etwas Enttäuschung breit - habe ich [...]]]></description>
			<content:encoded><![CDATA[<p>Nun bin ich schon so weit gekommen. Die Ergebnisse sind auch schon ganz passabel, aber können doch mit dem aktuellen Anspruch an 3D Spiele nicht mithalten. Die Berechnung der Voxellandschaft ist sehr CPU lastig und kann die vielen Vorteile der in der PSP verbauten Grafikhardware nicht nutzen.<br />
Ernüchternd macht sich etwas Enttäuschung breit - habe ich mir doch so viel vorgenommen&#8230;</p>
<p>Die Recherche in einigen Foren bringt mich dann zu dem Entschluss die Voxel in ihrem Space liegen zu lassen und sich mal ein wenig mit den 3D Grafikmöglichkeiten der PSP zu beschäftigen, denn diese Hardware ist ja - wie wir von vielen Spielen wissen - in der Lage sehr schöne Welten zum Vorschein zu bringen. Da wollen wir doch mal schauen, ob da auch was für unser Projekt dabei ist.</p>
<p>Es macht sich ein wenig vorsichtiger Enthusiasmus breit <img src='http://psp.anmabagima.de/wp-includes/images/smilies/icon_biggrin.gif' alt=':D' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://psp.anmabagima.de/2010/01/11/neustart/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Bildschirm aufräumen</title>
		<link>http://psp.anmabagima.de/2010/01/07/bildschirm-aufraumen/</link>
		<comments>http://psp.anmabagima.de/2010/01/07/bildschirm-aufraumen/#comments</comments>
		<pubDate>Thu, 07 Jan 2010 10:57:02 +0000</pubDate>
		<dc:creator>AnMaBaGiMa</dc:creator>
		
		<category><![CDATA[Einfache 3D Welten]]></category>

		<guid isPermaLink="false">http://psp.anmabagima.de/2010/01/07/bildschirm-aufraumen/</guid>
		<description><![CDATA[
                                                       [...]]]></description>
			<content:encoded><![CDATA[<link rel="stylesheet" type="text/css" href="http://www.anmabagima.de/blog/styles/shCore.css" />
<link rel="stylesheet" type="text/css" href="http://www.anmabagima.de/blog/styles/shThemeAnmabagima.css" /><script type="text/javascript" src="http://www.anmabagima.de/blog/scripts/shCore.js"></script><script type="text/javascript" src="http://www.anmabagima.de/blog/scripts/shBrushCpp.js"></script><script language="javascript">                                                          SyntaxHighlighter.config.bloggerMode = true;SyntaxHighlighter.config.clipboardSwf = "http://www.anmabagima.de/blog/scripts/clipboard.swf";SyntaxHighlighter.all();</script>Bisher haben wir immer wieder unsere Zeichenfläche mit der Landschaft voll&#8221;gemalt&#8221;. 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 &#8220;stehen&#8221; bleiben. Das liegt daran, dass wir den Bildschirm nicht aufräumen - also leeren - bevor wir neu zeichnen.<br />
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:<br />
<strong>Backbuffer</strong><br />
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 &#8220;verborgenen&#8221; 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.<br />
Zuerst definieren wir einen &#8220;flip&#8221; 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.<br />
Doch nun erstmal zu den neuen Definitionen in der Homebrew Basisklasse:</p>
<pre class="brush:cpp">
class ClHomebrew {
….
protected:
/*
* in welchen Puffer soll gezeichnet werden ?
*/
bool pg_drawframe;
/*
* ermitteln des adresse des zeichen Puffers
*/
unsigned char* pgGetVramAddr();
</pre>
<p>Die Implementierung für diese Methode sieht dann so aus:</p>
<pre class="brush:cpp">
unsigned char *ClHomebrew::pgGetVramAddr(){
if (pg_drawframe)
return VRAM_TOP + FRAMESIZE;
else
return VRAM_TOP;
}
</pre>
<p>Zusätzlich müssen wir den neuen Zeichenpuffer auch beim setzen der Bildpunkte berücksichtigen. Darum sieht die Methode setPixel nun wie folgt aus:</p>
<pre class="brush:cpp">
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;
}
</pre>
<p>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:<em>&lt;stdlib.h&gt;</em> und <em>&lt;pspdisplay.h&gt;</em></p>
<pre class="brush:cpp">
void ClPspVoxel::mainthread(){
unsigned char* drawBuffer;
drawBuffer = pgGetVramAddr();
//leer r&#228;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&#252;llt wird
pg_drawframe = pg_drawframe?false:true;
//nach jedem Zeichnungsvorgang darauf warten dass die PSP das Bild
//komplett ausgegeben hat
sceDisplayWaitVblankStart();
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://psp.anmabagima.de/2010/01/07/bildschirm-aufraumen/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Schwarz/Weiß war gestern</title>
		<link>http://psp.anmabagima.de/2010/01/05/schwarzweis-war-gestern/</link>
		<comments>http://psp.anmabagima.de/2010/01/05/schwarzweis-war-gestern/#comments</comments>
		<pubDate>Tue, 05 Jan 2010 17:04:54 +0000</pubDate>
		<dc:creator>AnMaBaGiMa</dc:creator>
		
		<category><![CDATA[Einfache 3D Welten]]></category>

		<guid isPermaLink="false">http://psp.anmabagima.de/2010/01/05/schwarzweis-war-gestern/</guid>
		<description><![CDATA[
                                                       [...]]]></description>
			<content:encoded><![CDATA[<link rel="stylesheet" type="text/css" href="http://www.anmabagima.de/blog/styles/shCore.css" />
<link rel="stylesheet" type="text/css" href="http://www.anmabagima.de/blog/styles/shThemeAnmabagima.css" /><script type="text/javascript" src="http://www.anmabagima.de/blog/scripts/shCore.js"></script><script type="text/javascript" src="http://www.anmabagima.de/blog/scripts/shBrushCpp.js"></script><script language="javascript">                                                          SyntaxHighlighter.config.bloggerMode = true;SyntaxHighlighter.config.clipboardSwf = "http://www.anmabagima.de/blog/scripts/clipboard.swf";SyntaxHighlighter.all();</script><br />
Heute ist Farbe angesagt. Darum werden wir nun ein paar Farbtöpfe auf unserer Landschaft &#8220;vergießen&#8221;. 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&#8230;<br />
Doch immer eins nach dem anderen.</p>
<p><strong>Erstmal die Farbe</strong></p>
<p>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&#215;512) und wie folgt aussieht:<br />
<img width="128" src="http://www.anmabagima.de/blog/images/landscape.png" height="128" /></p>
<p>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.</p>
<p>Die erweiterungen in der Klassendefinition:</p>
<pre class="brush:cpp">
class ClPspVoxel : public Cl2dHomebrew {
….
protected:
….
//neuer Parameter zur Aufnahme der gelesenen Daten
bool loadMap(const char* filename, unsigned int* data);
….
//H&#246;henkarte nun als Zeiger f&#252;r dynamische Speicher Reservierung
//unsigned int mapData[512][512]; //H&#246;henwerte der Karte
unsigned int* mapData;
unsigned int* texture;
</pre>
<p>Die Anpassungen in der Implentierung der Lademethode sehen dann wie folgt aus:</p>
<pre class="brush:cpp">
/*
* 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&#215;512 punkten aus
*/
bool ClPspVoxel::loadMap(const char* filename, unsigned int* data) {
….
//Zeile nun Punkt f&#252;r Punkt durchgehen und die Farbe in den Puffer &#252;bertragen
for (x = 0; x &lt; width; x++) {
u32 color = line[x];
//neuer Zugriff auf den Datenpuffer in linearer Form -&gt;zeile*maxBreite + spalte
//mapData[y][x] = color;
data[y*512 + x] = color;
}
</pre>
<p>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 <em>malloc.h</em> einbinden.Der Code hierfür sieht dann so aus:</p>
<pre class="brush:cpp">
bool ClPspVoxel::init(){
…..
//Speicher f&#252;r die H&#246;henkarte
mapData = (unsigned int*)malloc(sizeof(unsigned int)*512*512);
//H&#246;henkarte laden
loadMap(”altitude.png”, mapData);
//speicher f&#252;r die Textur
texture = (unsigned int*)malloc(sizeof(unsigned int)*512*512);
loadMap(”landscape.png”, texture);
</pre>
<p>Um den dynamisch reservierten Speicher wieder freizugeben, wenn er nicht mehr gebraucht wird - das ist ganz wichtig! - platzieren wir folgende zeilen in den Klassendestruktor:</p>
<pre class="brush:cpp">
free(mapData);
free(texture);
</pre>
<p>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:</p>
<pre class="brush:cpp">
void ClPspVoxel::render() {
….
//H&#246;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);
</pre>
<p>Das Ergebnis sieht schon ganz ordentlich aus:</p>
<p><center><img width="431" src="http://www.anmabagima.de/blog/images/voxelland_3.png" /></center><strong>Volle Kontrolle?</strong><br />
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: <em>pspctrl.h</em>.<br />
Nun können wir an das Ende der Bildbereichnung die Abfrage des PSP Pads legen:</p>
<pre class="brush:cpp">
void ClPspVoxel::render() {
….
//nach dem rendern fragen wir das pad ab.
//wurde der Analog-Stick bewegt ?
SceCtrlData pad;
sceCtrlPeekBufferPositive(&amp;pad, 1);
//die ausrichtung des analog sticks ist
//0 ganz links, 255 ganz rechts
//0 ganz oben und 255 ganz unten.
//f&#252;r eine Bewegung brauchen wir -x bis +x f&#252;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 &gt;&gt;= 5;
moveY &gt;&gt;= 5;
posY+=moveY;
posX+=moveX;
}
</pre>
<p><strong>Schweben</strong><br />
Nun wollen wir noch über unsere Landschaft &#8220;schweben&#8221;. 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-&#8221;gemalt&#8221; wurde&#8230;</p>
<pre class="brush:cpp">
void ClPspVoxel::render() {
…..
rayZ = 81 + (mapData[(posY&amp;511)*512 + (posX&amp;511)] &amp; 255); //384;
….
}while (rayZ &lt; currentAltitude);
}
// wenn der Bildschirm am oberen Rand erreicht ist, brauchen
//wir den strahl nicht weiter verfolgen…
if (pixelY &lt;= 1) break;
</pre>
<p>Viel Spaß bei den ersten &#8220;Flugversuchen&#8221; <img src='http://psp.anmabagima.de/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p></font></p>
]]></content:encoded>
			<wfw:commentRss>http://psp.anmabagima.de/2010/01/05/schwarzweis-war-gestern/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>

