Irrlicht - from Noob to Pro: Einfache Texturmanipulationen

Im vorhergehenden Abschnitt haben wir uns mit der Translation ,Rotation und Skalierung von Objekten beschäftigt.
In diesem Abschnitt will ich auf die Rotation, Translation und Skalierung der Texturen eines Objekts anhand eines kurzen Beispiels eingehen und aufzeigen, wie man dies bewerkstelligt.

Die Funktionen

Bearbeiten

Für die Translation, Rotation und Skalierung der Texturen verwenden wir in diesem Beispiel 3 Prozeduren von Irrlicht, welche die entsprechende Berechnung der Texturmatrix für uns vornehmen.
Im Gegensatz zum vorherigen Quellcodebeispiel müssen wir hier berücksichtigen, dass wir den Rotationswinkel der Textur nun in Radiant angeben müssen, um den entsprechenden Winkel zu erhalten. Da ein Rotationswinkel von 180 Grad nun 1 x π, ergibt, kann man das Verhältnis von Radiant zu Grad durch Teilung der beiden erreichen (180 ° : π = 57,29577951 Grad), was uns Irrlicht in der Header-Datei irrMath.h als Konstante bereistellt :

//! 32bit constant for converting from radians to degrees (formally known as GRAD_PI)
const f32 RADTODEG   = 180.0f / PI;

Somit gilt für die Umrechnung von Radiant nach Grad also Radiant * RADTODEG = Gradzahl

Rotation

Bearbeiten

Wir haben für die Rotation einer Textur 2 Funktionen zur Verfügung. Die erste ist

setTextureRotationCenter( f32 rotateRad )

und dreht uns unsere Textur grundsätzich um die Texturkoordinaten (UV) 0.5, 0,5, was den Mittelpunkt einer Textur mit einer Spannweite von (UV) 1.0, 1.0 entspricht. Sollte sich hier die Spannweite über 1.0 erhöhen, so muss die Textur durch Translation in mittige Lage gebracht werden. Diese Funktion verwendet die Einheit Radiant zur Angabe des Winkels.
Die zweite Funktion ist

setRotationDegrees( const vector3d<T>& rotation )

und verwendet die Einheit Grad zur Angabe des Rotationswinkels der Textur. Diese Funktion erwartet eine Variable vom Typ core::vector3d zur Übergabe des Winkels, wobei hier die Textur über die linke obere Ecke rotiert wird, was den UV-Koordinaten 0.0, 0.0 entspricht.

Translation

Bearbeiten
 
Das Fenster der Beispielanwendung

Die Tranlation der Textur erfolgt über die Angabe des Betrags, um welchen die Textur auf den UV-Koordinaten verschoben werden soll. Dies erfolgt über den Aufruf von

setTextureTranslate ( f32 x, f32 y )

Hier stellt die X-Achse den horizontalen Wert (U-Koordinate) und die Y-Achse den vertikalen Wert (V-Koordinate) dar.

Skalierung

Bearbeiten

Bei der Skalierung der Textur ist zu beachten, dass die Textur ab einer Skalierung von größer +1.0 oder kleiner -1.0 wiederholt wird, was bei der z.B. der Darstellung langer Mauern zum Einsatz kommt. Anwenden lässt sich die Skalierung mit

setTextureScale ( f32 sx, f32 sy )

wobei die X-Koordinate die horizontale Skalierung (U-Koordinate) und die Y-Koordinate die vertikale Skalierung (V-Koordinate) darstellt.

Beispielcode zur Anwendung

Bearbeiten

In diesem Beispiel werden zuerst drei Würfel erstellt und jeweils um 45 Grad gedreht, um die Textur besser sehen zu können. Danach wird jedem Würfel dieselbe Textur zugewiesen und bei dem linken Würfel zentrisch rotiert, beim rechten Würfel translationiert und beim oberen Würfel skaliert.
Hierzu der Quellcode :

//Einbinden der Header-Datei von Irrlicht
#include <irrlicht.h>

//Einbinden der Namespaces
using namespace irr;
using namespace core;
using namespace video;

//Die Hauptprozedur main()
int main()
{
	//Unser Irrlicht-Device erstellen und initialisieren
	IrrlichtDevice *device =
		createDevice( video::EDT_OPENGL, dimension2d<u32>(640, 480), 32,
			false, false, false, 0);

	//Konnte das Device erstellt werden ?
	if (!device)
		return 1; //Falls nicht, Fehlercode zurückgeben und Programm abbrechen

	//Den Text des Hauptfensters festlegen
	device->setWindowCaption(L"Rotation,Translation u. Skalierung einer Textur !");

	//Den Videotreiber erstellen und Zeiger aus dem Device abholen
	IVideoDriver* driver = device->getVideoDriver();	
	
	//Einen Szene-Manager erstellen und Zeiger aus dem Device abholen
	scene::ISceneManager* smgr = device->getSceneManager();
	
	//Erstellen der SceneNodes
	scene::ISceneNode * Linker_Wuerfel = smgr->addCubeSceneNode(2.0f);
	scene::ISceneNode * Rechter_Wuerfel = smgr->addCubeSceneNode(2.0f);
	scene::ISceneNode * Oberer_Wuerfel = smgr->addCubeSceneNode(2.0f);
	
	//Zuweisen des Materials
	Linker_Wuerfel->setMaterialTexture(0, driver->getTexture("IntP_Brick.png"));
	Rechter_Wuerfel->setMaterialTexture(0, Linker_Wuerfel->getMaterial(0).getTexture(0));
	Oberer_Wuerfel->setMaterialTexture(0, Linker_Wuerfel->getMaterial(0).getTexture(0));

	//Keine Lichtberechnung
	Linker_Wuerfel->setMaterialFlag(EMF_LIGHTING, false);
	Rechter_Wuerfel->setMaterialFlag(EMF_LIGHTING, false);
	Oberer_Wuerfel->setMaterialFlag(EMF_LIGHTING, false);

	//Position korrigieren
	Linker_Wuerfel->setPosition(core::vector3df(-2, -1, 3));
	Rechter_Wuerfel->setPosition(core::vector3df(2, -1, 3));
	Oberer_Wuerfel->setPosition(core::vector3df(0, 1.5, 3));

	//Um 45 Grad in Schräglage drehen
	Linker_Wuerfel->setRotation(core::vector3df(45.0f, 45.0f, 0));
	Rechter_Wuerfel->setRotation(core::vector3df(45.0f, -45.0f, 0));
	Oberer_Wuerfel->setRotation(core::vector3df(-45.0f, 45.0f, 0));
	
	//Eine Integer-Variable zum Speichern der letzten FPS
	int lastFPS = -1;

	//Unser 32Bit-Float zum Speichern des Texturwinkels in Radiant
	f32 fWinkel = 0.0;	

	//Unsere Variablen zur Zeitsteuerung
	u32 uiZuletztGestoppt = device->getTimer()->getTime(); //Aktuelle Zeit stoppen
	const u32 uiDeltaTime = 50; //Textur alle 50ms drehen

	//Einen GUI_Manager erstellen und Zeiger aus dem Device abholen
	gui::IGUIEnvironment* guienv = device->getGUIEnvironment();

	//Ein Text-Element definieren und an den GUI-Manager übergeben
	gui::IGUIStaticText* GUI_debug_text = guienv->addStaticText
		(L"",rect<s32>(5, 5, 300, 300),false,true,0,-1,false);

	//Eine Kamera erstellen
	smgr->addCameraSceneNode(0, core::vector3df(0,0,-2), core::vector3df(0,0,0));	

	//Während das Device aktiv ist ...
	while(device->run())
	{		
		//Szene beginnen
		driver->beginScene(true, true, SColor(3,150,203,255));
		
		//Nach Ablauf von Delta Time
		if (device->getTimer()->getTime() > (uiZuletztGestoppt + uiDeltaTime))
		{
			//Neuen Zeitpunkt merken
			uiZuletztGestoppt = device->getTimer()->getTime();
			//Winkel erhöhen
			fWinkel += 0.01f;
			//Nicht über 360 Grad (=6.3 Radians) drehen
			if (fWinkel > 6.3f) fWinkel =0.01f;
		}
		//Die Textur des linken Würfels drehen
		Linker_Wuerfel->getMaterial(0).getTextureMatrix(0).setTextureRotationCenter(fWinkel);

		//Alternativ kann auch mit folgender Zeile über die linke obere Ecke gedreht werden
		/*Linker_Wuerfel->getMaterial(0).getTextureMatrix(0).setRotationDegrees(
			core::vector3d<f32>(0.0f, 0.0f, fWinkel * RADTODEG)
			);*/
		
		//Die Textur des rechten Würfels verschieben
		Rechter_Wuerfel->getMaterial(0).getTextureMatrix(0).setTextureTranslate((2 * sin(fWinkel)),0.0f);

		//Die Textur des oberen Würfels skalieren
		Oberer_Wuerfel->getMaterial(0).getTextureMatrix(0).setTextureScale(sin(fWinkel),1.0f);
				
		//Dem Szenemanager sagen, dass er alle Nodes zeichnen soll
		smgr->drawAll();		

		//String erstellen
		core::stringw tmp(L"Rotation,Translation u. Skalierung einer Textur in Irrlicht\nTreiber : ");
		tmp += driver->getName();
		tmp += L"\n FPS: ["; tmp += driver->getFPS();
		tmp += "]\nDreiecke gezeichnet : ";
		tmp += driver->getPrimitiveCountDrawn();;
		tmp += "\nGrafikkarte ist von : ";
		tmp += driver->getVendorInfo();
		tmp += "\nLetzter Zeitstopp : ";
		tmp += uiZuletztGestoppt;
		tmp += "\nTexturwinkel (Grad): ";
		tmp += (fWinkel * RADTODEG);
		tmp += "\nTexturwinkel (Rad): ";
		tmp += fWinkel;
		tmp += "\nTranslation der Textur (U-Achse): ";
		tmp += (2 * sin(fWinkel));
		tmp += "\nSkalierung der Textur (U-Achse): ";
		tmp += sin(fWinkel);

		//String anzeigen
		GUI_debug_text->setText(tmp.c_str());		

		//Dem GUI-Manager sagen, dass er alle GUIs zeichnen soll 
		guienv->drawAll();

		//Szene beenden
		driver->endScene();
	}
	//Das Device freigeben
	device->drop();
	
	//Keinen Fehler zurückgeben
	return 0;
}