IRRLICHT - ubertini

Download Report

Transcript IRRLICHT - ubertini

MASSIMO UBERTINI
IRRLICHT
WWW.UBERTINI.IT
MODULO 1
IRRLICHT
Introduzione
Prima applicazione
Mondo 2D
Mondo 3D
Animazioni
Costruire una GUI
Collisioni
Picking
Billboarding
Terreno
Terrain rendering
Tetraedro
File
UI
Movimenti
Effetti speciali
Splitscreen
Mouse & Joystick
Menu
Per pixel lighting
Vertex e pixel shader
Prima applicazione Visual C#
Prima applicazione Visual Basic
IrrLicht
um 1 di 116
INTRODUZIONE
GENERALITÀ
È una libreria grafica tridimensionale, scritta in C++ e utilizzabile sia con questo linguaggio
sia con i linguaggi di .NET e, inoltre, con Java, Pyton e Ruby.
È completamente gratuita, oltre ad essere open source.
È liberamente modificabile in ogni sua parte senza il vincolo di rendere disponibile il codice
delle modifiche.
È liberamente utilizzabile in progetti di tipo commerciale, senza obbligo di corrispondere
alcuna royalty agli sviluppatori ma questi richiedono solo di essere citati tra i “credits” del
progetto sviluppato.
È un sistema cross platform, Windows, Linux, Mac OSX, Xbox ma occorre ricompilare il
codice.
Usa come driver grafico l’accelerazione DirectX, Open GL (Open Graphics Library), un
S/W render driver.
Può usare la programmazione della GPU (Graphics Processing Unit) con i linguaggi
seguenti.
HLSL (High Level Shading Language)
È un linguaggio sviluppato da Microsoft per la creazione di shader da usare in DirectX ed
è molto simile al linguaggio Cg di nVIDIA, permette di scrivere complessi calcoli grafici che
possono essere eseguiti molto velocemente dalla GPU e rappresenta, inoltre, il primo
passo per una pipeline grafica completamente programmabile.
GLSL (OpenGL Shading Language)
Conosciuto come GLsLang, è un linguaggio di programmazione ad alto livello per la
gestione delle unità shader di una GPU basato su linguaggio di programmazione C.
Lo scopo di questo linguaggio è quello di permettere ai programmatori un controllo più
diretto e immediato delle pipeline grafiche che non richiedesse l’uso di codice assembly o
di codici specifici, introdotto come estensione per la libreria grafica OpenGL.
È possibile trovare ogni informazione disponibile su IrrLicht all'indirizzo ufficiale.
http://irrlicht.sourceforge.net
È possibile trovare informazioni aggiuntive nella sezione Project Announcements del
forum di IrrLicht, all'indirizzo ufficiale.
http://irrlicht.sourceforge.net/phpBB2/index.php
I S/W ludici non sono solamente grafica 3D, dal punto di vista tecnico molte sono le
componenti importanti.
 La gestione di periferiche di gioco.
 Il codice di rete.
Una cosa sicuramente fondamentale è l’audio.
IrrLicht si sposa benissimo con Audiere, una libreria S/W che offre delle API (Application
IrrLicht
um 2 di 116
Programming Interface) di alto livello, per riprodurre i più svariati file audio: OGG VORBIS,
MP3, FLAC e WAV.
Audiere, come IrrLicht, è cross platform e può sfruttare diverse librerie audio di livello più
basso, come ad esempio Direct-Sound per Windows o OSS sotto Linux.
Audiere è un progetto open source reperibile al link
http://audiere.sourceforge.net
irrEdit è un SGE (Scene Graph Editor) per IrrLicht Engine e crea file con estensione IRR.
Il package si trova all’indirizzo ufficiale.
http://www.ambiera.com/irredit
Gli esempi sono sviluppati in C++ e Visual C# nell’ambiente MDE (Microsoft Development
Environment).
IrrLicht
um 3 di 116
PRIMA APPLICAZIONE
INTRODUZIONE
Nel progetto Visual Studio ci sono i seguenti file.
All’interno della cartella del progetto creare la sotto cartella LIBRERIE.
File IRRLICHT.EXP
Clic pulsante destro sul nome del progetto Aggiungi/Elemento esistente…
I:\IRRLICHT-1.6.1\IRRLICHT-1.6.1\LIB\WIN32-VISUALSTUDIO
File IRRLICHT.LIB
Clic pulsante destro sul nome del progetto Aggiungi/Elemento esistente…
I:\IRRLICHT-1.6.1\IRRLICHT-1.6.1\LIB\WIN32-VISUALSTUDIO
File MAIN.CPP
1. Inclusioni
Servono per dire al compilatore qual è il file header da considerare; quali sono i
namespace usati (irr, core, scene, video, io e gui).
#include <I:\irrlicht-1.6.1\irrlicht-1.6.1\include\irrlicht.h>
using namespace irr;
using namespace video;
using namespace scene;
using namespace core;
using namespace video;
using namespace io;
using namespace gui;
2. Inizializzazioni
IrrlichtDevice è la classe “fondamentale” del motore, per cui è possibile accedere ad ogni
componente e istanza del motore mentre è in run; da esso è possibile ottenere il controllo
del driver di rendering o del manager della scena.
All’atto di creazione del device si definiscono i particolari che istruiranno la creazione del
driver di rendering che è la vera e propria interfaccia con DirectX, OpenGL o S/W render,
quindi, per definire un IrrlichtDevice, si deve dichiarare un puntatore.
IrrlichtDevice *device;
Per creare un device si utilizza l’istruzione seguente.
IRRLICHT_API IrrlichtDevice* IRRCALLCONV createDevice(
video::E_DRIVER_TYPE deviceType = video::EDT_SOFTWARE,
IrrLicht
um 4 di 116
const core::dimension2d<u32>& windowSize = core::dimension2d<s32>(640,480),
u32 bits = 16,
bool fullscreen = false,
bool stencilbuffer=false,
bool vsync=false,
IEventReceiver* receiver = 0,
const char* sdk_version_do_not_use = IRRLICHT_SDK_VERSION);
deviceType
È un valore definito tra i valori della enum E_DRIVER_TYPE, definita nel namespace
video, essa descrive il tipo di driver e quindi se esso è Direct3D, OpenGL o altro.
I valori assegnabili sono i seguenti.
EDT_NULL,
EDT_SOFTWARE,
EDT_SOFTWARE2,
EDT_DIRECT3D8,
EDT_DIRECT3D9,
EDT_OPENGL
windowsize
È la dimensione della finestra di rendering e a schermo intero è la risoluzione di rendering.
bits
Indica la profondità di colore, di default è settata a 16 bit, prediligendo la velocità al
dettaglio grafico, tuttavia si può mettere a 32 per sfruttare colorazione a 32 bit.
fullscreen
Indica se la modalità schermo intero è attivata oppure no.
stencilbuffer
È un booleano che indica se è abilitato lo stencil buffer; DirectX e OpenGL possono
lavorare con questo buffer che fa da vero e proprio utensile nei rendering in cui appaiono
stencil reflections e stencil shadows, IrrLicht supporta lo stenciling per quanto riguarda le
stencil shadows.
vsync
È un booleano che se è settato a true rende bloccante il rendering.
Vsync
È la vertical syncronization, significa che una volta richiamata la funzione di rendering
l’applicazione si blocca e attende che il drawing su schermo sia completo.
receiver
È il ricevitore degli eventi, IrrLicht implementa al suo interno alcune classi che si occupano
di gestire gli eventi ad esempio provenienti dalla pressione di un tasto della tastiera o dal
movimento del mouse.
Quindi si deve creare un oggetto visibile dalla funzione di creazione del device e si
assegna il riferimento alla createDevice.
L'ultimo parametro indica la versione del motore non va variato e neanche utilizzato.
Esempio, istanziare il device, il driver video e lo scene manager.
È creato un device con driver DirectX9, il cui rendering avviene in una finestra di
visualizzazione in 640*480 pixel ed una profondità di colore di 16 bit, senza l’ausilio dello
IrrLicht
um 5 di 116
stencilbuffer e senza sincronizzazione verticale.
È stato omesso il parametro dell’event receiver poiché non si è definita alcuna classe di
event receiving.
I puntatori a IVideoDriver e ISceneManager servono per la gestione della scena che varia
da applicazione ad applicazione.
int main(void)
{
// setup iniziali
IrrlichtDevice *device =
createDevice(EDT_DIRECT3D9, core::dimension2d<u32>(640, 480), 16, false);
if (device == 0) return 1; // impossibile creare il driver
IVideoDriver* driver = device->getVideoDriver();
ISceneManager* smgr = device->getSceneManager();
3. Ciclo principale
Si occupa di gestire gli input che riceve dall’esterno e disegnare la scena 3D.
Il driver dà inizio alla scena e la conclude, lo scene Manager (smgr) si occupa di stabilire
cosa dev’essere manipolato e/o disegnato.
Ordinando una drawAll lo scene manager organizza i nodi e li disegna nel giusto ordine
per visualizzare correttamente, ad esempio, le ombre sui muri.
In questo caso non si disegna nulla, tramite la funzione drawAll.
// ciclo principale
while(device->run())
{
driver->beginScene(true, true, video::SColor(255,100,101,140));
smgr->drawAll();
driver->endScene();
}
4 Clean up
Il ciclo precedente è interrotto quando il device non è più in esecuzione, ovvero quando la
condizione run diventa falsa.
Questo avviene quando, ad esempio, si preme ALT+F4 per terminare l’applicazione.
Prima di uscire è buona regola di programmazione rilasciare tutte le risorse utilizzate.
Tutte le fasi di pulizia e deallocazione della memoria sono automatiche e gestite in
maniera trasparente dal motore.
Ovviamente in questa fase saranno inserite anche tutte le operazioni di rilascio di oggetti
creati al di fuori di IrrLicht.
L’ultima istruzione è il rilascio del device, prima di terminare l’applicazione, si deve liberare
dalla memoria dati che ha precedentemente allocato e che risiedono nella visibilità del
device.
IrrLicht è completamente OO (Object Oriented), quindi molte funzioni sono implementate
come metodi di classe.
// rilascio delle risorse ed uscita
device->drop();
return (0);
}
IrrLicht
um 6 di 116
Il motore mette a disposizione una funzione che accetta una struttura dati più dettagliata.
IRRLICHT_API IrrlichtDevice* IRRCALLCONV createDeviceEx(
const SIrrlichtCreationParameters& parameters);
La struttura SirrlichtCreationParameters è così composta.
video::E_DRIVER_TYPE DriverType;
core::dimension2d<s32> WindowSize;
u32 Bits;
bool Fullscreen;
bool Stencilbuffer;
bool Vsync;
bool AntiAlias;
IEventReceiver* EventReceiver;
s32 WindowId;
const char* SDK_version_do_not_use;
La maggior parte dei parametri è uguale alla precedente funzione.
bool AntiAlias
Abilitando (true) l’antialiasing, si abilita all'atto della creazione il driver che dev’essere o
Direct3D98 o Direct3D9, entrambi in full screen, con l’antialiasing a 2X con accuratezza 1.
L’antialiasing è una tecnica che si usa per ammorbidire le geometrie evitando ad esempio
il tratto netto delle linee e dei poligoni, oltre che a sfumare comunque il texturemapping
IrrLicht
um 7 di 116
anche se questa funzione è svolta principalmente dai filtri di correzione come
l’anisotropico.
L’utilizzo dei filtri antialiasing deteriora enormemente le prestazioni, quindi verificare che
sia davvero necessario prima di abilitarlo.
WindowId
È un window handle, se si volesse, ad esempio, creare il device irr in un’altra finestra
diversa da quella standard, si deve assegnare a questa variabile il valore HWND della
finestra che si vuole.
Esempio, creazione di un device con gli extended parameters.
SIrrlichtCreationParameters param;
param.DriverType = video::EDT_DIRECT3D9;
param.WindowSize = core::dimension2d<s32>(1024, 768);
param.Bits =32;
param.Fullscreen = true;
param.Stencilbuffer = false;
param.Vsync = false;
param.AntiAlias = false;
param.EventReceiver = 0;
param.WindowId = 0;
param.SDK_version_do_not_use = IRRLICHT_SDK_VERSION;
IrrlichtDevice *device =createDeviceEx(param);
IrrLicht
um 8 di 116
MONDO 2D
VISUALIZZARE UN’IMMAGINE
Nel progetto Visual Studio ci sono i seguenti file.
All’interno della cartella del progetto creare le sotto cartelle LIBRERIE e MEDIA.
File MAIN.CPP
#include <I:\irrlicht-1.6.1\irrlicht-1.6.1\include\irrlicht.h>
#include <iostream>
using namespace irr;
using namespace video;
using namespace scene;
using namespace core;
using namespace video;
using namespace io;
using namespace gui;
int main(void)
{
// setup iniziali
IrrlichtDevice *device =
createDevice(EDT_DIRECT3D9, dimension2d<u32>(640, 480), 16, false);
if (device == 0) return 1; // impossibile creare il driver
video::IVideoDriver* driver = device->getVideoDriver();
device->setWindowCaption(L"Grafica 2D");
// titolo della finestra
// tutte e tre le immagini sono nella texture 2ddemo.png
video::ITexture* images = driver->getTexture("Media/2ddemo.png");
driver->makeColorKeyTexture(images, core::position2d<s32>(0,0));
gui::IGUIFont* font = device->getGUIEnvironment()->getBuiltInFont();
gui::IGUIFont* font2 =
device->getGUIEnvironment()->getFont("Media/fonthaettenschweiler.bmp");
core::rect<s32> imp1(349,15,385,78);core::rect<s32> imp2(387,15,423,78);
// ciclo principale
while(device->run() && driver)
{
if (device->isWindowActive())
{
u32 time = device->getTimer()->getTime();
driver->beginScene(true, true, video::SColor(255,120,102,136));
// visualizza il mondo
driver->draw2DImage(images, core::position2d<s32>(50,50),
core::rect<s32>(0,0,342,224), 0,
video::SColor(255,255,255,255), true);
// visualizza la prima flying imp
IrrLicht
um 9 di 116
driver->draw2DImage(images, core::position2d<s32>(164,125),
(time/500 % 2) ? imp1 : imp2, 0,video::SColor(255,255,255,255), true);
// visualizza la seconda flying imp
driver->draw2DImage(images, core::position2d<s32>(270,105),
(time/500 % 2)?imp1:imp2,0,video::SColor(255,(time) % 255,255,255),true);
// visualizza il testo
if (font)
font->draw(L"Visualizza la grafica 2D",
core::rect<s32>(130,10,300,50),video::SColor(255,255,255,255));
// draw some other text
if (font2)
font2->draw(L"3D graphics",core::rect<s32>(130,20,300,60),
video::SColor(255,time % 255,time % 255,255));
// disegna il logo
driver->draw2DImage(images, core::position2d<s32>(10,10),
core::rect<s32>(354,89,442,151));
// disegna un rettangolo trasparente sotto il cursore
core::position2d<s32> m = device->getCursorControl()->getPosition();
driver->draw2DRectangle(video::SColor(100,255,255,255),
core::rect<s32>(m.X-20, m.Y-20, m.X+20, m.Y+20));
driver->endScene();
}
}
device->drop(); return (0);
// rilascio delle risorse ed uscita
}
IrrLicht
um 10 di 116
Render To Texture
Nel progetto Visual Studio ci sono i seguenti file.
All’interno della cartella del progetto creare le sotto cartelle LIBRERIE e MEDIA.
File MAIN.CPP
#include <I:\irrlicht-1.6.1\irrlicht-1.6.1\include\irrlicht.h>
#include <iostream>
using namespace irr;
using namespace video;
using namespace scene;
using namespace core;
using namespace video;
using namespace io;
using namespace gui;
int main(void)
{
// setup iniziali
IrrlichtDevice *device =
createDevice(EDT_DIRECT3D9, dimension2d<u32>(640, 480), 16, false);
if (device == 0) return 1; // impossibile creare il driver
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
gui::IGUIEnvironment* env = device->getGUIEnvironment();
scene::IAnimatedMeshSceneNode* fairy = smgr->addAnimatedMeshSceneNode(
smgr->getMesh("Media/faerie.md2"));
if (fairy)
{
fairy->setMaterialTexture(0,driver->getTexture("Media/faerie2.bmp"));
fairy->setMaterialFlag(video::EMF_LIGHTING, true);
fairy->getMaterial(0).Shininess = 20.0f;
fairy->setPosition(core::vector3df(-10,0,-100));
fairy->setMD2Animation ( scene::EMAT_STAND );
}
// aggiungo la luce bianca
smgr->addLightSceneNode(0, core::vector3df(-15,5,-105),
video::SColorf(1.0f, 1.0f, 1.0f));
// setto l'ambiente
smgr->setAmbientLight(video::SColor(60,60,60,60));
// creazione della videocamera
scene::ICameraSceneNode* fpsCamera = smgr->addCameraSceneNodeFPS();
fpsCamera->setPosition(core::vector3df(-50,50,-150));
// disabilito il cursore del mouse
IrrLicht
um 11 di 116
device->getCursorControl()->setVisible(false);
// creo il cubo
scene::ISceneNode* test = smgr->addCubeSceneNode(60);
// rotazione del cubo e setto la luce
scene::ISceneNodeAnimator* anim = smgr->createRotationAnimator(
core::vector3df(0.3f, 0.3f,0));
test->setPosition(core::vector3df(-100,0,-100));
test->setMaterialFlag(video::EMF_LIGHTING, false);
test->addAnimator(anim);
anim->drop();
// titolo della finestra
device->setWindowCaption(L"Render to Texture e Specular Highlights");
video::ITexture* rt = 0;
scene::ICameraSceneNode* fixedCam = 0;
if (driver->queryFeature(video::EVDF_RENDER_TO_TARGET))
{ rt = driver->addRenderTargetTexture(core::dimension2d<u32>(256,256), "RTT1");
test->setMaterialTexture(0, rt);
fixedCam = smgr->addCameraSceneNode(0, core::vector3df(10,10,-80),
core::vector3df(-10,10,-100));
}
else
{
gui::IGUISkin* skin = env->getSkin();
gui::IGUIFont* font = env->getFont("Media/fonthaettenschweiler.bmp");
if (font) skin->setFont(font);
gui::IGUIStaticText* text = env->addStaticText(
L"L'hardware non è abilitato. RTT Disabilitato.",
core::rect<s32>(150,20,470,60));
text->setOverrideColor(video::SColor(100,255,255,255));
}
int lastFPS = -1;
while(device->run())
if (device->isWindowActive())
{
driver->beginScene(true, true, 0);
if (rt)
{
driver->setRenderTarget(rt, true, true, video::SColor(0,0,0,255));
test->setVisible(false);
smgr->setActiveCamera(fixedCam);
smgr->drawAll();
// setto il colore di background
driver->setRenderTarget(0, true, true, 128);
test->setVisible(true);
smgr->setActiveCamera(fpsCamera);
}
smgr->drawAll();
env->drawAll();
driver->endScene();
int fps = driver->getFPS();
if (lastFPS != fps)
{ core::stringw str = L"Render to Texture e Specular Highlights. Esempio ";
str += " FPS:";
str += fps;
device->setWindowCaption(str.c_str());
lastFPS = fps;
}
IrrLicht
um 12 di 116
}
// rilascio delle risorse ed uscita
device->drop();
return (0);
}
IrrLicht
um 13 di 116
MONDO 3D
INTRODUZIONE
Realizzare lo scheletro di un FPS (First Person Shooter), grazie ad una mappa.
Quello che si deve fare è caricare la mappa che si deve esplorare e aggiungere le relative
istruzioni di caricamento nella porzione d’inizializzazione del motore.
CARICARE LA MAPPA
Aggiungere la mappa con la seguente istruzione.
// caricamento della mappa
device->getFileSystem()->addZipFileArchive("Media/map-20kdm2.pk3");
Tutta la parte di decompressione e i relativi algoritmi sono trasparenti al programmatore
che si può concentrare semplicemente sul contenuto del file, senza sapere come questo è
reso disponibile.
Il nome della mappa è 20KDM2.BSP, l’estensione BSP (Binary Space Partitioning) indica
un formato pre compilato, in cui tutte le informazioni geometriche sono sistemate in
maniera tale da rendere semplice l’esclusione dalla fase di disegno, rendering, delle parti
di mappa che sicuramente non sono visibili.
Evitare di effettuare calcoli per zone non visibili consente di mantenere alto il frame rate, in
pratica il numero di fotogrammi per secondo disegnati a schermo.
Implementare manualmente un algoritmo BSP e il relativo formato dati sarebbe un
compito tutt’altro che facile.
In IrrLicht tutto ciò è fornito gratuitamente al programmatore, il caricamento vero e proprio
della mappa avviene con le seguenti righe di codice.
IAnimatedMesh* mesh = smgr->getMesh("20kdm2.bsp");
ISceneNode* node = 0;
if (mesh) node = smgr->addOctTreeSceneNode(mesh->getMesh(0));
È creata una struttura geometrica, mesh, tramite la funzione getMesh dello scene
manager, questa funzione accetta in ingresso una stringa contenente il nome del file che
contiene la mesh.
Lo scene manager è utilizzato per agganciare la mesh appena creata alla scena corrente,
al fine di poterla disegnare a video quando sarà invocata la drawAll.
Per questo compito è utilizzata la funzione addOctTreeSceneNode.
Questa funzione consente di utilizzare la struttura BSP caricata con la mesh e di usufruire
dei relativi vantaggi in termini di velocità.
È possibile utilizzare, ottenendo gli stessi risultati per quanto riguarda la visualizzazione,
anche la funzione addAnimatedMeshSceneNode.
Questa funzione tuttavia gestisce tutta la struttura geometrica caricata, senza eliminare le
parti non visibili e per questo risulta più lenta.
Il nodo ISceneNode* node aggiunto alla scena principale contiene la mesh ottenuta
tramite la funzione getMesh.
Il parametro numerico 0, passato come argomento, indica che sarà preso il primo frame di
animazione della mesh.
Tuttavia, in questo caso, la mesh della mappa non ha animazioni, per cui vi è un solo
frame e non è possibile che prendere quello.
IrrLicht
um 14 di 116
VIDEOCAMERA
Per visualizzare qualcosa a schermo, occorre aggiungere alla scena corrente un nodo che
contenga una videocamera.
Le videocamere possono essere di differenti tipi: da quelle semplici che forniscono
solamente la visualizzazione, a quelle più complesse che gestiscono anche gli input da
tastiera, si usa in questo caso una videocamera di quest’ultimo tipo.
Quello che si deve fare è un FPS.
// creazione della videocamera
ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS();
camera->setPosition(vector3df(1100,700,1500));
Una videocamera che consente di muovere la visuale in maniera simile a quella di molti
videogiochi in prima persona.
Si può controllare la direzione dello sguardo muovendo il mouse, mentre ci si sposta in
avanti e indietro con le frecce SU e GIU della tastiera, sempre con le frecce DESTRA e
SINISTRA si può effettuare lo scorrimento laterale.
Per migliorare l’aspetto dell’applicazione si può eliminare il cursore del mouse che
altrimenti resterebbe fisso al centro della finestra di visualizzazione.
L'istruzione per fare questo è la seguente.
// disabilito il cursore del mouse
device->getCursorControl()->setVisible(false);
Una possibile alternativa potrebbe essere quella di visualizzare un cursore differente dalla
classica freccia bianca di Windows.
In molti giochi, ad esempio, è presente una piccola croce che simula il mirino di un’arma.
In ogni caso il primo passo per la gestione del cursore è quello di ottenere un puntatore al
relativo controllore, tramite la funzione getCursorControl.
COLLISIONI
Se si provasse il codice così com’è, quello che si ottiene sarebbe la possibilità di muoversi
all'interno della mappa “a volo d’angelo”, in altre parole sarebbe possibile spostarsi in
qualsiasi punto della mappa, a qualsiasi altezza, passando anche attraverso muri.
Per evitare questo comportamento e dare il senso di una reale camminata, si deve in
qualche modo gestire le collisioni.
L’applicazione deve, in breve, reagire all’impatto della videocamera con i muri, bloccando
lo spostamento e simulando così la presenza di un ostacolo fisico.
Quello che bisogna fare innanzitutto è creare un oggetto di tipo ITriangleSelector.
Aggiungere quindi al sorgente le seguenti righe di codice.
// gestione delle collisioni
ITriangleSelector* selector = 0;
selector = smgr->createOctTreeTriangleSelector(mesh->getMesh(0), node, 128);
Si è creato un selettore di tipo OctTree che è una struttura dati che definisce una forma
tridimensionale ottimizzandone il contenuto.
Ogni porzione di spazio è divisa in cubi.
A sua volta ogni cubo può essere diviso in 8 cubi più piccoli uguali tra loro.
Allo stesso modo un BSP divide lo spazio in entità binarie.
Laddove è necessaria una maggiore precisione l’entità è divisa in 2, in 4, in 8 e così via.
OctTree e BSP sono alla base dei motori che gestiscono grafica 3D in tempo reale.
L’unica cosa che interessa sapere in questo caso è che questo tipo di selettore è
IrrLicht
um 15 di 116
ottimizzato per lavorare congiuntamente con i file BSP.
Proprio il tipo di file che si è aggiunto alla scena principale, sempre tramite una funzione di
tipo OctTree.
Come linea generale utilizzando una mesh OctTree si usa un selettore dello stesso tipo.
SCENE ANIMATORS
L’ultima cosa da fare è aggiungere alla videocamera FPS, creata in precedenza, quello
che è chiamato un “animatore”, in altre parole un oggetto che si occupa di conferire al
nodo della scena cui è assegnato un comportamento.
L’animatore utilizzerà il selettore creato in precedenza per gestire le collisioni della
videocamera con la mesh della mappa.
// creo l'animator per la gestione delle collisioni
ISceneNodeAnimator* anim = smgr->createCollisionResponseAnimator(selector,camera);
camera->addAnimator(anim);
anim->drop();
La funzione di creazione createCollisionResponseAnimator accetta in ingresso 6
parametri.
1. Il selettore della mesh per la gestione delle collisioni.
2. L’oggetto che sarà gestito da questo animatore, in questo caso la videocamera, è il
nodo racchiuso dall’ellissoide.
3. La dimensione dell’oggetto passata tramite un vector3df, i tre raggi dell’ellissoide.
4. Il vettore dell’accelerazione di gravità.
5. L’incremento della gravità per secondo.
6. La traslazione del punto centrale dell’oggetto rispetto al nodo cui è assegnato, è lo
spostamento dell’ellissoide rispetto al centro della mesh.
È sufficiente chiamare la createCollisionResponseAnimator passando solo i primi 2
parametri, gli altri hanno valori di default.
A questo punto si può controllare la videocamera coi tasti direzionali e il mouse come si è
fatto sinora ma il suo comportamento sarà modificato dall’animatore, non è più possibile,
infatti, oltrepassare i muri o volare in quanto si è mantenuti a terra dalla gravità.
La cosa bella del meccanismo degli animatori è che è possibile applicarli a qualsiasi nodo
della scena, non solo alla videocamera.
Una volta creato, l’animatore è aggiunto alla videocamera tramite la funzione
addAnimator.
Fatto questo è possibile eliminare il riferimento all’animatore appena creato tramite la
funzione drop, l’animatore continuerà ad esistere in quanto una sua copia è stata
assegnata ad un oggetto attivo nella scena.
#include <I:\irrlicht-1.6.1\irrlicht-1.6.1\include\irrlicht.h>
#include <iostream>
using namespace irr;
using namespace video;
using namespace scene;
using namespace core;
int main(void)
{
// setup iniziali
IrrlichtDevice *device =
createDevice(EDT_DIRECT3D9, dimension2d<u32>(640, 480), 16,false, false, false, 0);
IVideoDriver* driver = device->getVideoDriver();
ISceneManager* smgr = device->getSceneManager();
device->setWindowCaption(L"Ambienti 3D con IrrLicht");
IrrLicht
um 16 di 116
// caricamento della mappa
device->getFileSystem()->addZipFileArchive("Media/map-20kdm2.pk3");
IAnimatedMesh* mesh = smgr->getMesh("20kdm2.bsp");
ISceneNode* node = 0;
if (mesh) node = smgr->addOctTreeSceneNode(mesh->getMesh(0));
// creazione della videocamera
ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS();
camera->setPosition(vector3df(1100,700,1500));
// elimino il cursore del mouse
device->getCursorControl()->setVisible(false);
// gestione delle collisioni
ITriangleSelector* selector = 0;
selector = smgr->createOctTreeTriangleSelector(mesh->getMesh(0), node, 128);
// creo l'animator per la gestione delle collisioni
ISceneNodeAnimator* anim = smgr->createCollisionResponseAnimator(selector,camera);
camera->addAnimator(anim);
anim->drop();
// ciclo fondamentale del programma: disegno della scena
while(device->run())
{
driver->beginScene(true, true, SColor(0,200,200,200));
smgr->drawAll(); driver->endScene();
}
// rilascio delle risorse
device->drop(); return (0);
}
IrrLicht
um 17 di 116
Ogni oggetto identificato in un mondo 3D è composto dall’unione di entità geometriche.
L’entità per eccellenza è il triangolo.
Nei moderni motori grafici gli oggetti sono identificati da una collezione di triangoli che
vanno a comporre una mesh.
Per esempio, una figura è una collezione di triangoli a formare la mesh.
Ogni nodo base contiene quindi una mesh che darà al driver le informazioni per
renderizzare ogni cosa poligonale che si vede videogame.
Una mesh è complessa, per velocizzare la situazione si usa la BoundingBox, è una
scatola che racchiude la mesh, è utilizzata per effettuare calcoli di visibilità e collisione sui
nodi della scena.
In generale si può dire che una mesh è composta da diversi materiali, a ogni materiale è
associata una o più texture.
Per esempio, una stanza composta da pavimento pareti e soffitto, sui pavimenti saranno
visualizzate piastrelle, sulle pareti carta da parati e sul soffitto intonaco.
Quindi alla mesh si associa tre materiali, ciascuno con le proprie textures così da
semplificare il rendering della stanza.
Inoltre, quando si esegue un rendering avviene prima di tutto il sorting dei poligoni in base
al materiale così da poter renderizzare geometrie omogenee e soprattutto creare un
ordine di visualizzazione tra i materiali solidi e in alpha blending.
IrrLicht
um 18 di 116
ANIMAZIONI
INTRODUZIONE
Esempio, animazione.
#include <I:\irrlicht-1.6.1\irrlicht-1.6.1\include\irrlicht.h>
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
int main(void)
{ // setup iniziali
IrrlichtDevice* device =
createDevice(video::EDT_OPENGL,core::dimension2d<u32>(640,
480),16,false,false,false,0);
if (device == 0)
return 1; // impossibile creare il driver
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
// carico la mesh
scene::IAnimatedMeshSceneNode* anms = smgr->addAnimatedMeshSceneNode(
smgr->getMesh("Media/sydney.md2"));
anms->setMaterialFlag(video::EMF_LIGHTING, false);
anms->setMaterialTexture(0, driver->getTexture("Media/sydney.bmp"));
// aggiungo la videocamera
smgr->addCameraSceneNode(0, vector3df(0,30,-40), vector3df(0,5,0));
// disabilito il cursore del mouse
device->getCursorControl()->setVisible(false);
// inserisco un titolo per la finestra di visualizzazione
device->setWindowCaption(L"Ambienti 3D con IrrLicht. Esempio: animazione");
// ciclo principale
while(device->run())
{
driver->beginScene(true, true, video::SColor(255,113,113,133));
smgr->drawAll();
driver->endScene();
}
// rilascio le risorse e esco
device->drop();
return (0);
}
IrrLicht
um 19 di 116
Esempio, animazione.
#include <I:\irrlicht-1.6.1\irrlicht-1.6.1\include\irrlicht.h>
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
int main(void)
{
IrrlichtDevice *device =
createDevice( video::EDT_SOFTWARE, dimension2d<u32>(640, 480), 32,
false, false, false, 0);
if (!device) return 1;
device->setWindowCaption(L"Hello, World!");
/* memorizzo il pointer del video driver, dello SceneManager, e della GUI */
IVideoDriver* driver = device->getVideoDriver();
ISceneManager* smgr = device->getSceneManager();
IGUIEnvironment* guienv = device->getGUIEnvironment();
/* il testo è posizionato nell’angolo sinistro in alto (10,10)
e nell’angolo in basso a destra (200,22).*/
guienv->addStaticText(L"Ciao, mondo! ",rect<s32>(10,10,260,22), false);
IAnimatedMesh* mesh = smgr->getMesh("Media/sydney.md2");
if (!mesh)
{
device->drop();
IrrLicht
um 20 di 116
return 1;
}
IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode( mesh );
if (node)
{
node->setMaterialFlag(EMF_LIGHTING, false);
node->setMD2Animation(scene::EMAT_STAND);
node->setMaterialTexture( 0, driver->getTexture("Media/sydney.bmp") );
}
// posiziono la videocamera nello spazio 3D alla posizione (0,10,-40)
smgr->addCameraSceneNode(0, vector3df(0,30,-40), vector3df(0,5,0));
/* chiudo la finestra con i tasti ALT+F4
ciclo principale */
while(device->run())
{
driver->beginScene(true, true, SColor(255,100,101,140));
smgr->drawAll();
guienv->drawAll();
driver->endScene();
}
// rilascio delle risorse
device->drop();
return (0);
}
CONTROLLARE L’ANIMAZIONE
Il problema è che IrrLicht non è stato istruito sul come gestire l’animazione.
Il file SYDNEY.MD2 contiene, una di seguito all’altra, tutte i movimenti possibili per questa
mesh.
Per fermare Sydney si deve aggiungere la seguente riga di codice.
anms->setFrameLoop(0, 0);
L’istruzione setFrameLoop imposta la ripetizione ciclica dell’animazione compresa tra i
fotogrammi iniziale e finale, passati come parametro.
In questo caso si sta dicendo a IrrLicht di ciclare sempre sullo stesso frame, il primo, ecco
perché, eseguendo ora il codice, l’immagine apparirà immobile.
Per ottenere una plastica corsa si deve inserire la seguente riga di codice.
anms->setFrameLoop(320, 360);
Far ciclare l’animazione tra i fotogrammi 320 e 360 che contengono il movimento di corsa.
L’utilizzo di questi indici per gestire un’animazione potrebbe sembrare scomodo ma è
dettato dal fatto che, spesso, i formati dei file 3D non vanno molto oltre questo livello di
complessità.
Inoltre, una gestione così basilare permette d’implementare qualsiasi tipo di gestione
personalizzata da parte del programmatore, ampliandone le possibilità di scelta.
Un altro parametro che permette di controllare le animazioni, è la velocità con la quale i
frame si succedono a schermo.
anms->setAnimationSpeed(30);
È possibile variare questa velocità, più alto è il valore passato come parametro maggiore
sarà la rapidità di successione dei frame.
IrrLicht
um 21 di 116
EVENTI
Gli input dell’utente sono gestiti con un sistema basato su eventi, questi eventi sono, ad
esempio, input come “è stato premuto il tasto X”, “è stato mosso il mouse”.
Un altro tipo di eventi sono quelli generati dal sistema d’interfaccia grafica, in questo caso,
gli eventi sono del tipo “è stato premuto un pulsante”, “è stata incrementata di 10 la
scrollbar.
Il meccanismo è il seguente.
1. Derivazione: si deriva una classe dalla classe IEventReceiver e se ne ridefinisce il
metodo OnEvent in modo che gestisca l’input.
2. Gestore: s’istanzia un oggetto della classe creata al punto precedente e lo si passa
come parametro della createDevice, la funzione che istanzia il dispositivo di
visualizzazione.
Supponendo di chiamare il gestore di eventi personalizzato MyEventReceiver, è
sufficiente modificare il codice dell’esempio precedente in modo che la creazione del
device avvenga con le seguenti righe di codice.
MyEventReciever receiver;
device = createDevice(video::EDT_OPENGL,core::dimension2d<s32>(640, 480),
16, false, false, false, &receiver);
In pratica è modificato, rispetto a prima, l’ultimo parametro della funzione.
GESTIONE PERSONALIZZATA
Il gestore degli eventi dev’essere un oggetto di una classe derivata da IEventReciever.
IEventReciever presenta un solo metodo pubblico virtuale: OnEvent.
OnEvent è un metodo richiamato internamente da IrrLicht ogni qualvolta si verifica una
qualsiasi delle situazioni che sono riconosciute come evento.
Per questo motivo OnEvent ha un parametro in ingresso, event di tipo SEvent che serve a
capire, nel corpo del metodo, quale sia stato l'evento scatenante della chiamata attuale.
Non è il programmatore a dovere passare il parametro event ma è IrrLicht a farlo, con una
chiamata interna, il compito del programmatore è quello di gestire l’evento se è di un tipo
che interessa, altrimenti non si deve fare nulla.
Per questa ragione, tipicamente, il metodo OnEvent presenta al suo interno un costrutto
switch che effettua le operazioni specificate.
Se quello che si deve ottenere dal codice è fare correre l0immagine tenendo premendo il
tasto SPAZIO, si deve scrivere il seguente codice.
class MyEventReceiver : public IEventReceiver
{ public:
scene::IAnimatedMeshSceneNode* anms;
virtual bool OnEvent(const SEvent& event)
{ if ((anms != 0) && (event.EventType ==irr::EET_KEY_INPUT_EVENT))
{ switch(event.KeyInput.Key)
{ case KEY_SPACE:
if (event.KeyInput.PressedDown) anms->setFrameLoop(320, 360);
else anms->setFrameLoop(0, 0);
return true;
break;
}
}
return false;
}
};
IrrLicht
um 22 di 116
La prima cosa che è fatta nella OnEvent è il controllo delle condizioni iniziali di esecuzione
dello switch.
In particolare dev’essere già esistente il nodo contenente la mesh da animare anms.
Inoltre, il tipo di evento dev’essere della famiglia dei “tasti premuti”, in altre parole di tipo
seguente.
irr::EET_KEY_INPUT_EVENT
Non tutti gli eventi sono generati dalla pressione di tasti sulla tastiera, per cui questo
controllo è d’obbligo.
Lo switch poi effettua la sua scelta in base al valore del campo event.KeyInput.Key che è
la parte della struttura passata come parametro che contiene l’informazione sul tasto
premuto.
In questo esempio interessa KEY_SPACE, la barra spaziatrice.
È corretto utilizzare uno switch per controllare una sola condizione, infatti, permette di
espandere facilmente il funzionamento del IEventReciever con la gestione di altri tasti.
All’interno del case, è effettuato un ulteriore controllo, IrrLicht permette di distinguere tra gli
eventi “tasto premuto” e “tasto rilasciato”, questo offre una notevole potenza nella gestione
degli eventi da tastiera.
Tuttavia potrebbe generare problemi: la pressione dello SPAZIO, infatti, scatena in questo
modo due eventi, premuto e rilasciato, mentre nell’esempio interessa solamente uno alla
volta.
Per questo motivo si deve impostare il loop su “corsa” quando SPAZIO è premuto, mentre
sarà messo a “fermo” quando SPAZIO è rilasciato.
IrrLicht
um 23 di 116
COSTRUIRE UNA GUI
INTRODUZIONE
1. GUI (Graphics User Interface)
Creare un'interfaccia grafica è semplicissimo, l’applicazione mantiene la solita struttura, si
deve però mantenere un puntatore a IGUIEnvironment.
// imposto la finestra
device->setEventReceiver(&receiver);
device->setWindowCaption(L"SI o NO?");
video::IVideoDriver* driver = device->getVideoDriver();
// ottengo un puntatore all'interfaccia grafica
IGUIEnvironment* env = device->getGUIEnvironment();
2. Pulsanti
Il puntatore env consente di aggiungere elementi grafici e funzionali quali pulsanti,
scrollbar e caselle di testo.
// aggiungiamo due pulsanti
// Il primo parametro contiene le dimensioni del pulsante
// Il secondo parametro la finestra genitore
// Il terzo l'ID necessario al gestore di eventi
// Il quarto il testo da mostrare
env->addButton(rect<s32>(10,10,630,230), 0, 101, L"SI");
env->addButton(rect<s32>(10,250,630,470), 0, 102, L"NO");
3. Loop principale
Anche per un’applicazione che presenta la sola interfaccia grafica, senza scene 3D, la
struttura prevista rimane la medesima.
// ciclo principale
while(device->run()&& driver)
{
if (device->isWindowActive())
{ driver->beginScene(true, true,video::SColor(255,113,113,133));
env->drawAll();
driver->endScene();
}
}
// rilascio le risorse e esco
device->drop();
4. Gestione degli eventi
La classe derivata da IeventReciever prevede la gestione di eventi generati dalla GUI, la
condizione iniziale prevede il test col valore EET_GUI_EVENT.
class MyEventReceiver : public IEventReceiver
{ public: IrrlichtDevice* device;
virtual bool OnEvent(const SEvent& event)
{ if (event.EventType == EET_GUI_EVENT)
{ // ottengo il puntatore all' IGUIEnvironment
IrrLicht
um 24 di 116
u32 id = event.GUIEvent.Caller->getID();
IGUIEnvironment* env = device->getGUIEnvironment();
5. Pulsanti
Dopo la if è previsto uno switch, questo determina il comportamento vero e proprio da
tenere, in questo caso sono visualizzate delle MessageBox.
switch(event.GUIEvent.EventType)
{ case EGET_BUTTON_CLICKED:
// controllo sugli id dei pulsanti id == 101 --> pulsante "Si"
if (id == 101) { env->addMessageBox(L"Hai scelto...",L"SI!"); return true; }
// id == 102 --> pulsante "No"
if (id == 102) { env->addMessageBox(L"Hai scelto...",L"NO!"); }
}
}
return false;
}
};
// esempio: pulsanti
#include <I:\irrlicht-1.6.1\irrlicht-1.6.1\include\irrlicht.h>
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
class MyEventReceiver : public IEventReceiver
{ public:
IrrlichtDevice* device;
virtual bool OnEvent(const SEvent& event)
{ if (event.EventType == EET_GUI_EVENT)
{ // ottengo il puntatore all' IGUIEnvironment
u32 id = event.GUIEvent.Caller->getID();
IGUIEnvironment* env = device->getGUIEnvironment();
switch(event.GUIEvent.EventType)
{ case EGET_BUTTON_CLICKED:
// controllo sugli id dei pulsanti id == 101 --> pulsante "Si"
if (id == 101) { env->addMessageBox(L"Hai scelto...",L"SI!"); return true; }
// id == 102 --> pulsante "No"
if (id == 102) { env->addMessageBox(L"Hai scelto...",L"NO!"); }
}
}
return false;
}
};
int main(void)
{ MyEventReceiver receiver;
// setup iniziali
IrrlichtDevice* device =
createDevice(video::EDT_OPENGL,core::dimension2d<u32>(640,
480),16,false,false,false,&receiver);
if (device == 0)
return 1; // impossibile creare il driver
// imposto la finestra
IrrLicht
um 25 di 116
device->setEventReceiver(&receiver);
device->setWindowCaption(L"SI o NO?");
video::IVideoDriver* driver = device->getVideoDriver();
// ottengo un puntatore all'interfaccia grafica
IGUIEnvironment* env = device->getGUIEnvironment();
// aggiungiamo due pulsanti
// Il primo parametro contiene le dimensioni del pulsante
// Il secondo parametro la finestra genitore
// Il terzo l'ID necessario al gestore di eventi
// Il quarto il testo da mostrare
env->addButton(rect<s32>(10,10,630,230), 0, 101, L"SI");
env->addButton(rect<s32>(10,250,630,470), 0, 102, L"NO");
// disabilito il cursore del mouse
device->getCursorControl()->setVisible(false);
// inserisco un titolo per la finestra di visualizzazione
device->setWindowCaption(L"Ambienti 3D con IrrLicht. Esempio: pulsanti");
while(device->run()&& driver)
// ciclo principale
{
if (device->isWindowActive())
{ driver->beginScene(true, true,video::SColor(255,113,113,133));
env->drawAll(); driver->endScene();
}
}
// rilascio le risorse e esco
device->drop(); return (0);
}
IrrLicht
um 26 di 116
COLLISIONI
INTRODUZIONE
Uso del metodo approssimato per il calcolo della collisione tra videocamera e ambiente
circostante, nel progetto Visual Studio ci sono i seguenti file.
// esempio: nessuna collisione
#include <I:\irrlicht-1.6.1\irrlicht-1.6.1\include\irrlicht.h>
#include <iostream>
using namespace irr;
using namespace video;
using namespace scene;
using namespace core;
int main(void)
{
// setup iniziali
IrrlichtDevice *device =
createDevice(EDT_DIRECT3D9, dimension2d<u32>(640, 480), 16, false);
if (device == 0)
return 1; // impossibile creare il driver
IVideoDriver* driver = device->getVideoDriver();
ISceneManager* smgr = device->getSceneManager();
// inizializzo il livello
device->getFileSystem()->addZipFileArchive("Media/map-20kdm2.pk3");
IAnimatedMesh* q3levelmesh = smgr->getMesh("20kdm2.bsp");
ISceneNode* q3node = 0;
if (q3levelmesh)
q3node = smgr->addOctTreeSceneNode(q3levelmesh->getMesh(0));
// creo la videocamera
ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS(0,100.0f,300.0f);
// disabilito il cursore del mouse
device->getCursorControl()->setVisible(false);
// inserisco un titolo per la finestra di visualizzazione
device->setWindowCaption(L"Ambienti 3D con IrrLicht. Esempio: nessuna
collisione");
// ciclo principale
while(device->run())
{
driver->beginScene(true, true, 0);
smgr->drawAll();
driver->endScene();
}
IrrLicht
um 27 di 116
// rilascio le risorse e esco
device->drop();
return (0);
}
Si è creato il device, il driver e lo SceneManager, si è attaccato al file system la mesh del
livello che si vuole caricare e si è creato un nodo con essa.
Eliminato il puntatore del mouse e si è creata una videocamera di tipo FPS per potersi
spostare nel livello.
Il ciclo principale si occupa unicamente di disegnare l’intera scena.
Muovendosi con i tasti direzionali si ottiene un effetto di volo d'angelo: è possibile andare
in qualsiasi direzione, non si è soggetti a gravità e si oltrepassano i muri che, invece,
dovrebbero essere solidi.
Per evitare questo comportamento e dare il senso di una reale camminata, si devono
gestire le collisioni.
L’applicazione deve, in breve, reagire all’impatto della videocamera con i muri, bloccando
lo spostamento e simulando così la presenza di un ostacolo fisico.
VIDEOCAMERA E METODO DELLA DISTANZA
Per rilevare le collisioni è necessario creare un oggetto di tipo TriangleSelector da
associare alla mesh.
// gestione delle collisioni
ITriangleSelector* selector = 0;
if (q3node)
IrrLicht
um 28 di 116
{ q3node->setPosition(vector3df(-1370,-130,-1400)); //sposto la mesh
selector=smgr->createOctTreeTriangleSelector(q3levelmesh->getMesh(0), q3node, 128);
q3node->setTriangleSelector(selector);
selector->drop();
}
L’ITriangleSelector è la base per la collision detection: oggetti di questo tipo sono utilizzati
sia coi metodi esatti sia con quelli approssimati.
Si utilizza un metodo di questo secondo tipo per la gestione della videocamera FPS.
Quello che interessa è dare la sensazione d’ingombro della videocamera, piuttosto che
associarla ad una mesh ben definita.
Per questo motivo si può approssimarla con una bounding box o una sfera.
In realtà IrrLicht implementa un algoritmo di approssimazione un po’ più preciso che
permette di specificare un ellissoide anziché una sfera.
Un ellissoide è un’ellisse in tre dimensioni per la quale è necessaria specificare tre raggi,
uno per ogni asse, ovviamente se proprio si dovesse avere bisogno di una sfera, sarà
sufficiente fornire tre raggi uguali.
Si associa un particolare comportamento alla videocamera aggiungendo al suo nodo un
opportuno animatore: un oggetto di tipo ISceneNodeAnimator.
Questo oggetto si occupa di modificare il movimento della videocamera quando questa
collide con l’ambiente circostante.
In particolare, è modificata la traiettoria in maniera tale da non farle oltrepassare le mesh
con cui collide.
È questo comportamento che darà la sensazione di solidità dei muri del livello.
L’animatore utilizzato in questo caso è di tipo CollisionResponseAnimator e consente
d’inserire anche un valore per l’accelerazione gravitazionale.
// creo l'animator per la gestione delle collisioni
ISceneNodeAnimator* anim=smgr->createCollisionResponseAnimator(selector, camera);
camera->addAnimator(anim);
anim->drop();
È istanziato l’animatore, successivamente lo si aggiunge al nodo della videocamera
camera e quindi lo si rilascia drop in quanto sarà gestito automaticamente dallo scene
manager.
Da notare come la stessa sorte sia toccata anche al triangleselector istanziato in
precedenza.
// esempio: collisione videocamera-livello
#include <I:\irrlicht-1.6.1\irrlicht-1.6.1\include\irrlicht.h>
#include <iostream>
using namespace irr;
using namespace video;
using namespace scene;
using namespace core;
int main(void)
{ // setup iniziali
IrrlichtDevice *device =
createDevice(EDT_DIRECT3D9, dimension2d<u32>(640, 480), 16, false);
if (device == 0)
return 1; // impossibile creare il driver
IVideoDriver* driver = device->getVideoDriver();
ISceneManager* smgr = device->getSceneManager();
// inizializzo il livello
IrrLicht
um 29 di 116
device->getFileSystem()->addZipFileArchive("Media/map-20kdm2.pk3");
IAnimatedMesh* q3levelmesh = smgr->getMesh("20kdm2.bsp");
ISceneNode* q3node = 0;
if (q3levelmesh)
q3node = smgr->addOctTreeSceneNode(q3levelmesh->getMesh(0));
// creo il triangolo selector per la mesh del livello
ITriangleSelector* selector = 0;
if (q3node)
{
q3node->setPosition(vector3df(-1370,-130,-1400)); //sposto la mesh
selector = smgr->createOctTreeTriangleSelector(q3levelmesh->getMesh(0),
q3node, 128);
q3node->setTriangleSelector(selector);
selector->drop();
}
// creo la videocamera
ICameraSceneNode* camera = smgr>addCameraSceneNodeFPS(0,100.0f,300.0f);
// creo l'animator per la gestione delle collisioni
ISceneNodeAnimator* anim = smgr>createCollisionResponseAnimator(selector,camera);
camera->addAnimator(anim);
anim->drop();
// disabilito il cursore del mouse
device->getCursorControl()->setVisible(false);
// inserisco un titolo per la finestra di visualizzazione
device->setWindowCaption(L"Ambienti 3D con IrrLicht. Esempio: collisione
videocamera-livello");
// ciclo principale
while(device->run())
{
driver->beginScene(true, true, 0);
smgr->drawAll();
driver->endScene();
}
// rilascio le risorse e esco
device->drop();
return (0);
}
IrrLicht
um 30 di 116
// esempio due
#include <I:\irrlicht-1.6.1\irrlicht-1.6.1\include\irrlicht.h>
#include <iostream>
using namespace irr;
using namespace video;
using namespace scene;
using namespace core;
int main(void)
{
// setup iniziali
IrrlichtDevice *device =
createDevice(EDT_DIRECT3D9, dimension2d<u32>(640, 480), 16, false);
if (device == 0) return 1; // impossibile creare il driver
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
// inizializzo il livello
device->getFileSystem()->addZipFileArchive("Media/map-20kdm2.pk3");
scene::IAnimatedMesh* mesh = smgr->getMesh("20kdm2.bsp");
scene::ISceneNode* node = 0;
if (mesh)node = smgr->addOctTreeSceneNode(mesh->getMesh(0), 0, -1, 1024);
if (node)node->setPosition(core::vector3df(-1300,-144,-1249));
smgr->addCameraSceneNodeFPS();
// disabilito il cursore del mouse
device->getCursorControl()->setVisible(false);
int lastFPS = -1;
while(device->run())
IrrLicht
um 31 di 116
{
if (device->isWindowActive())
{
driver->beginScene(true, true, video::SColor(255,200,200,200));
smgr->drawAll();
driver->endScene();
int fps = driver->getFPS();
if (lastFPS != fps)
{
core::stringw str = L"Ambiente 3D con Irrlicht. Esempio [";
str += driver->getName();
str += "] FPS:";
str += fps;
device->setWindowCaption(str.c_str());
lastFPS = fps;
}
}
else device->yield();
}
// rilascio le risorse e esco
device->drop();
return (0);
}
IrrLicht
um 32 di 116
PICKING
INTRODUZIONE
Utilizzare il CollisionResponseAnimator è molto utile in fase di sperimentazione o debug
oppure per applicazioni abbastanza semplici.
In generale, tuttavia, il comportamento di un dato elemento andrà programmato in maniera
più esatta, per conformarsi alle necessità dell’applicazione che si sta creando.
Proprio per questo motivo è disponibile un oggetto di classe IsceneCollisionManager che
si occupa di gestire a basso livello le collisioni tra mesh.
Questo collision manager, ottenibile dallo scene manager tramite la funzione
getSceneCollisionManager, fornisce informazioni, è possibile sapere, ad esempio, il punto
esatto di contatto tra due mesh o il triangolo interessato dalla collisione.
Manipolando opportunamente queste informazioni si possono ottenere comportamenti
molto raffinati come, ad esempio, la creazione di un vero e proprio motore fisico che
gestisca gli oggetti secondo la loro velocità, accelerazione e massa.
Per esempio, disegnare in rosso i bordi del triangolo puntato dalla videocamera.
Per farlo si usa questa tecnica: creare una linea che congiunga la videocamera con il
punto preciso che sta inquadrando.
Di seguito si chiede a IrrLicht di fornire il punto d’intersezione di questa retta con la mesh
del livello nonché il triangolo della mesh che lo contiene.
Questo triangolo sarà disegnato a schermo, evidenziato in rosso.
Il codice che realizza questo comportamento è il seguente.
// preparo il materiale per il triangolo
SMaterial material;
material.Lighting = false;
// codice per evidenziare il triangolo guardato
line3d<f32> line;
line.start = camera->getPosition();
line.end = line.start +(camera->getTarget() - line.start).normalize() * 1000.0f;
vector3df intersection;
triangle3df tri;
if (smgr->getSceneCollisionManager()->getCollisionPoint(line, selector, intersection, tri))
{ driver->setTransform(ETS_WORLD, matrix4());
driver->setMaterial(material);
driver->draw3DTriangle(tri, SColor(0,255,0,0));
}
Come prima cosa è istanziato un materiale fittizio (SMaterial material) cui è disabilitata
l’illuminazione.
Questo serve in seguito quando è chiamata la draw3DTriangle.
All’interno del ciclo principale del programma (while(device> run()) {...} ) è creato un
oggetto di tipo line3d che rappresenta un segmento nello spazio.
Gli estremi del segmento sono così fissati.
 Inizio (line.start): posizione della videocamera.
 Fine (line.end): il punto visto dalla videocamera che si trova a distanza 1000 da essa.
È poi chiamata la funzione getCollisionPoint che prende in ingresso la linea creata (line) e
il ITriangleSelector della mesh del livello (selector) e restituisce, se esistono, il punto
d’intersezione (intersection) e il triangolo che lo contiene (tri).
In caso di collisione rilevata le coordinate del triangolo sono riportate in coordinate coerenti
col mondo (matrice ETS_WORLD), è disabilitata la luce impostando il materiale creato
IrrLicht
um 33 di 116
all’inizio e, infine, è disegnato in rosso il triangolo trovato tramite la draw3DTriangle.
La tecnica di selezionare un oggetto in base a ciò che è inquadrato è detta picking ed è
fondamentale in qualsiasi gioco 3D.
Si pensi solo a quando si seleziona un’unità del giocoi o a quando si mira per sparare in
un FPS qualsiasi.
// esempio: picking
#include <I:\irrlicht-1.6.1\irrlicht-1.6.1\include\irrlicht.h>
#include <iostream>
using namespace irr;
using namespace video;
using namespace scene;
using namespace core;
int main(void)
{ // setup iniziali
IrrlichtDevice *device =
createDevice(EDT_DIRECT3D9, dimension2d<u32>(640, 480), 16, false);
if (device == 0)
return 1; // impossibile creare il driver
IVideoDriver* driver = device->getVideoDriver();
ISceneManager* smgr = device->getSceneManager();
// inizializzo il livello
device->getFileSystem()->addZipFileArchive("Media/map-20kdm2.pk3");
IAnimatedMesh* q3levelmesh = smgr->getMesh("20kdm2.bsp");
ISceneNode* q3node = 0;
if (q3levelmesh)
q3node = smgr->addOctTreeSceneNode(q3levelmesh->getMesh(0));
// creo il triangolo selector per la mesh del livello
ITriangleSelector* selector = 0;
if (q3node)
{
q3node->setPosition(vector3df(-1370,-130,-1400)); //sposto la mesh
selector = smgr->createOctTreeTriangleSelector(q3levelmesh->getMesh(0), q3node, 128);
q3node->setTriangleSelector(selector);
selector->drop();
}
// creo la videocamera
ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS(0,100.0f,300.0f);
// creo l'animator per la gestione delle collisioni
ISceneNodeAnimator* anim = smgr->createCollisionResponseAnimator(selector,camera);
camera->addAnimator(anim);
anim->drop();
// disabilito il cursore del mouse
device->getCursorControl()->setVisible(false);
// inserisco un titolo per la finestra di visualizzazione
device->setWindowCaption(L"Ambienti 3D con IrrLicht. Esempio: picking");
// preparo il materiale per la texture
SMaterial material;
material.Lighting = false;
//ciclo principale
while(device->run())
{
driver->beginScene(true, true, 0);
smgr->drawAll();
// codice per evidenziare il triangolo guardato
line3d<f32> line;
IrrLicht
um 34 di 116
line.start = camera->getPosition();
line.end = line.start + (camera->getTarget() - line.start).normalize() * 1000.0f;
vector3df intersection;
triangle3df tri;
ISceneNode* p;
if (smgr->getSceneCollisionManager()->getCollisionPoint(
line,selector,intersection,tri))
{
driver->setTransform(ETS_WORLD, matrix4());
driver->setMaterial(material);
driver->draw3DTriangle(tri, SColor(0,255,0,0));
}
driver->endScene();
}
// rilascio le risorse e esco
device->drop();
return (0);
}
IrrLicht
um 35 di 116
BILLBOARDING
INTRODUZIONE
IrrLicht supporta nativamente il billboarding attraverso la classe IbillboardSceneNode che
permette di visualizzare una immagine 2D e modificarne numerosi parametri.
// creo un oggetto per il billboarding
IBillboardSceneNode * bill = smgr->addBillboardSceneNode();
// modifico parametri come trasparenza, immagine mostrata e dimensione
bill->setMaterialType(EMT_TRANSPARENT_ADD_COLOR );
bill->setMaterialTexture(0, driver->getTexture("Media/particle.bmp"));
bill->setMaterialFlag(EMF_LIGHTING, false);
bill->setSize(dimension2d<f32>(40.0f, 60.0f));
Per visualizzare un'immagine in billboarding è necessario specificare un punto
dell'ambiente 3D tramite la funzione setPosition.
// ottengo il punto di intersezione col livello
vector3df intersection;
triangle3df tri;
// imposto il billboarding nel punto trovato
bill->setPosition(intersection);
// esempio: billboarding
#include <I:\irrlicht-1.6.1\irrlicht-1.6.1\include\irrlicht.h>
#include <iostream>
using namespace irr;
using namespace video;
using namespace scene;
using namespace core;
int main(void)
{ // setup iniziali
IrrlichtDevice *device =
createDevice(EDT_DIRECT3D9, dimension2d<u32>(640, 480), 16, false);
if (device == 0)
return 1; // impossibile creare il driver
IVideoDriver* driver = device->getVideoDriver();
ISceneManager* smgr = device->getSceneManager();
// inizializzo il livello
device->getFileSystem()->addZipFileArchive("Media/map-20kdm2.pk3");
IAnimatedMesh* q3levelmesh = smgr->getMesh("20kdm2.bsp");
ISceneNode* q3node = 0;
if (q3levelmesh)
q3node = smgr->addOctTreeSceneNode(q3levelmesh->getMesh(0));
// creo il triangolo selector per la mesh del livello
ITriangleSelector* selector = 0;
if (q3node)
{
q3node->setPosition(vector3df(-1370,-130,-1400)); //sposto la mesh
selector = smgr->createOctTreeTriangleSelector(q3levelmesh->getMesh(0), q3node, 128);
IrrLicht
um 36 di 116
q3node->setTriangleSelector(selector);
selector->drop();
}
//creo la videocamera
ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS(0,100.0f,300.0f);
//creo l'animator per la gestione delle collisioni
ISceneNodeAnimator* anim = smgr->createCollisionResponseAnimator(selector,camera);
camera->addAnimator(anim);
anim->drop();
// disabilito il cursore del mouse
device->getCursorControl()->setVisible(false);
// inserisco un titolo per la finestra di visualizzazione
device->setWindowCaption(L"Ambienti 3D con IrrLicht. Esempio: billboarding");
// preparo il materiale per la texture
SMaterial material;
material.Lighting = false;
// setup del billboarding
IBillboardSceneNode * bill = smgr->addBillboardSceneNode();
bill->setMaterialType(EMT_TRANSPARENT_ADD_COLOR );
bill->setMaterialTexture(0, driver->getTexture("Media/particle.bmp"));
bill->setMaterialFlag(EMF_LIGHTING, false);
bill->setSize(dimension2d<f32>(40.0f, 60.0f));
//ciclo principale
while(device->run())
{
driver->beginScene(true, true, 0);
smgr->drawAll();
// codice per evidenziare il triangolo guardato
line3d<f32> line;
line.start = camera->getPosition();
line.end = line.start + (camera->getTarget() - line.start).normalize() * 1000.0f;
vector3df intersection;
triangle3df tri;
if (smgr->getSceneCollisionManager()->getCollisionPoint(line,selector,intersection,tri))
{
driver->setTransform(ETS_WORLD, matrix4());
driver->setMaterial(material);
driver->draw3DTriangle(tri, SColor(0,255,0,0));
}
// billboarding
bill->setPosition(intersection);
driver->endScene();
}
//Rilascio le risorse e esco
device->drop();
return (0);
}
IrrLicht
um 37 di 116
Nel progetto Visual Studio ci sono i seguenti file.
All’interno della cartella del progetto creare le sotto cartelle LIBRERIE e MEDIA.
File MAIN.CPP
/* descrive 2 metodi per rilevare le collisioni
1. rilevamento automatico
2. triangolo picking */
#include <I:\irrlicht-1.6.1\irrlicht-1.6.1\include\irrlicht.h>
#include <iostream>
using namespace irr;
using namespace video;
using namespace scene;
using namespace core;
enum
{
// si usa ISceneNode ID per indicare la scene node che è
// non bucata da getSceneNodeAndCollisionPointFromRay()
ID_IsNotPickable = 0,
// si usa questo flag in ISceneNode ID per indicare che la
// scene node può essere bucata con il raggio di selezione
IDFlag_IsPickable = 1 << 0,
// si usa questo flag in ISceneNode ID per indicare che la
// scene node è highlighted
IDFlag_IsHighlightable = 1 << 1
};
int main(void)
{
// setup iniziali
IrrlichtDevice *device =
createDevice(EDT_DIRECT3D9, core::dimension2d<u32>(640, 480), 16, false);
if (device == 0)
return 1; // impossibile creare il driver
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
device->getFileSystem()->addZipFileArchive("Media/map-20kdm2.pk3");
scene::IAnimatedMesh* q3levelmesh = smgr->getMesh("20kdm2.bsp");
scene::IMeshSceneNode* q3node = 0;
if (q3levelmesh)
q3node = smgr->addOctTreeSceneNode(q3levelmesh->getMesh(0), 0,
IDFlag_IsPickable);
// crea il triangolo selector
IrrLicht
um 38 di 116
scene::ITriangleSelector* selector = 0;
if (q3node)
{
q3node->setPosition(core::vector3df(-1350,-130,-1400));
selector = smgr->createOctTreeTriangleSelector(q3node->getMesh(), q3node, 128);
q3node->setTriangleSelector(selector);
}
// inserisco la prima persona sulla scena, jump speed di 3 unità per secondo
// uso una gravità di (0, -10, 0)
scene::ICameraSceneNode* camera =
smgr->addCameraSceneNodeFPS(0, 100.0f, .3f, ID_IsNotPickable, 0, 0, true, 3.f);
camera->setPosition(core::vector3df(50,50,-60));
camera->setTarget(core::vector3df(-70,30,-60));
if (selector)
{
scene::ISceneNodeAnimator* anim = smgr->
createCollisionResponseAnimator(selector, camera,
core::vector3df(30,50,30),core::vector3df(0,-10,0), core::vector3df(0,30,0));
selector->drop();
camera->addAnimator(anim);
anim->drop();
}
device->getCursorControl()->setVisible(false);
// aggiungo il billboard
scene::IBillboardSceneNode * bill = smgr->addBillboardSceneNode();
bill->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR );
bill->setMaterialTexture(0, driver->getTexture("Media/particle.bmp"));
bill->setMaterialFlag(video::EMF_LIGHTING, false);
bill->setMaterialFlag(video::EMF_ZBUFFER, false);
bill->setSize(core::dimension2d<f32>(20.0f, 20.0f));
bill->setID(ID_IsNotPickable);
// aggiungo le tre animazioni
scene::IAnimatedMeshSceneNode* node = 0;
// aggiungo il file faerie.md2
node = smgr->addAnimatedMeshSceneNode(smgr->getMesh("Media/faerie.md2"),
0, IDFlag_IsPickable | IDFlag_IsHighlightable);
node->setPosition(core::vector3df(-70,-15,-120));
node->setScale(core::vector3df(2, 2, 2));
node->setMD2Animation(scene::EMAT_POINT);
node->setAnimationSpeed(20.f);
video::SMaterial material;
material.setTexture(0, driver->getTexture("Media/faerie2.bmp"));
material.Lighting = true;
material.NormalizeNormals = true;
node->getMaterial(0) = material;
// adesso creo il triangolo selector
selector = smgr->createTriangleSelector(node);
node->setTriangleSelector(selector);
selector->drop();
// il file dwarf.x usa un'animazione scheletrica ma senza skinning
node = smgr->addAnimatedMeshSceneNode(smgr->getMesh("Media/dwarf.x"),
0, IDFlag_IsPickable | IDFlag_IsHighlightable);
node->setPosition(core::vector3df(-70,-66,0));
node->setRotation(core::vector3df(0,-90,0));
node->setAnimationSpeed(20.f);
selector = smgr->createTriangleSelector(node);
IrrLicht
um 39 di 116
node->setTriangleSelector(selector);
selector->drop();
// il file ninja.b3d
node = smgr->addAnimatedMeshSceneNode(smgr->getMesh("Media/ninja.b3d"),
0, IDFlag_IsPickable | IDFlag_IsHighlightable);
node->setScale(core::vector3df(10, 10, 10));
node->setPosition(core::vector3df(-70,-66,-60));
node->setRotation(core::vector3df(0,90,0));
node->setAnimationSpeed(10.f);
node->getMaterial(0).NormalizeNormals = true;
selector = smgr->createTriangleSelector(node);
node->setTriangleSelector(selector);
selector->drop();
material.setTexture(0, 0);
material.Lighting = false;
// aggiungo la luce
scene::ILightSceneNode * light = smgr->addLightSceneNode(0, core::vector3df(60,100,400), video::SColorf(1.0f,1.0f,1.0f,1.0f), 600.0f);
light->setID(ID_IsNotPickable);
scene::ISceneNode* highlightedSceneNode = 0;
scene::ISceneCollisionManager* collMan = smgr->getSceneCollisionManager();
int lastFPS = -1;
while(device->run())
if (device->isWindowActive())
{
driver->beginScene(true, true, 0);
smgr->drawAll();
if (highlightedSceneNode)
{highlightedSceneNode->setMaterialFlag(video::EMF_LIGHTING, true);
highlightedSceneNode = 0;
}
core::line3d<f32> ray;
ray.start = camera->getPosition();
ray.end = ray.start + (camera->getTarget() - ray.start).normalize() * 1000.0f;
core::vector3df intersection;
core::triangle3df hitTriangle;
scene::ISceneNode * selectedSceneNode =
collMan->getSceneNodeAndCollisionPointFromRay(
ray,intersection, hitTriangle, IDFlag_IsPickable,0);
if(selectedSceneNode)
{
bill->setPosition(intersection);
driver->setTransform(video::ETS_WORLD, core::matrix4());
driver->setMaterial(material);
driver->draw3DTriangle(hitTriangle, video::SColor(0,255,0,0));
if((selectedSceneNode->getID() & IDFlag_IsHighlightable) ==
IDFlag_IsHighlightable)
{
highlightedSceneNode = selectedSceneNode;
highlightedSceneNode>setMaterialFlag(video::EMF_LIGHTING, true);
}
}
driver->endScene();
int fps = driver->getFPS();
if (lastFPS != fps)
{
core::stringw str = L"Rilevamento collisioni. Esempio [";
IrrLicht
um 40 di 116
str += driver->getName();
str += "] FPS:";
str += fps;
device->setWindowCaption(str.c_str());
lastFPS = fps;
}
}
device->drop();
return (0);
}
IrrLicht
um 41 di 116
TERRENO
INTRODUZIONE
IrrLicht prevede la possibilità di effettuare il TR (Terrain Rendering) in maniera nativa, con
tutte le ottimizzazioni per rendere il disegno a schermo della scena più veloce possibile.
È possibile visualizzare un terreno, con tanto di texture, a partire da una mappa di altezze
(HeightMap ) che non è altro che un file grafico in toni di grigio.
È come se si vedesse una mappa del terreno che si vuole visualizzare, le zone più scure
corrispondono a livelli più bassi del terreno, mentre quelle più chiare sono i rilievi.
Dopo avere ricostruito il terreno a partire dalla HeightMap, è possibile applicarvi delle
texture per rendere più reale l’effetto ottenuto.
Ad esempio, la texture che rappresenta un terreno verde, un deserto e un lago.
Le texture sono applicate per mezzo di un fattore di scala che può adattarsi al livello di
dettaglio che si vuole ottenere.
Ad esempio, se si prevede d’inquadrature dall’alto, si può accontentarsi di un livello medio
di scalatura, per una scena in terza persona, si deve, optare per un livello più elevato.
L’algoritmo, per passare dalla HeightMap all’effettiva visualizzazione finale, è raffinato,
evita, infatti, che ci si possa imbattere in forme che siano la trasposizione semplicemente
della HeightMap.
Differenza di altezza fra zone vicine saranno trasformate in pendenze più o meno ripide
che creano una continuità su tutto il terreno.
// esempio: Terrain Rendering
#include <I:\irrlicht-1.6.1\irrlicht-1.6.1\include\irrlicht.h>
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
#define WINDOW_TITLE L"Esempio di Terrain Rendering"
int main(void)
{ // setup iniziali
IrrlichtDevice* device = reateDevice(video::EDT_OPENGL,core::dimension2d<u32>
(640, 480),16,false,false,false,0);
if (device == 0)
return 1; // impossibile creare il driver
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
// forzo la creazione di texture a 32 bit
driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, true);
// aggiungo e posiziono una videocamera controllabile coi tasti freccia
IrrLicht
um 42 di 116
scene::ICameraSceneNode* camera =
smgr->addCameraSceneNodeFPS(0,100.0f,1200.0f);
camera->setPosition(core::vector3df(1900*2,255*2,3700*2));
camera->setTarget(core::vector3df(2397*2,343*2,2700*2));
camera->setFarValue(12000.0f);
// disabilito il cursore del mouse
device->getCursorControl()->setVisible(false);
Si è forzato la creazione di texture a 32 bit e disabilitato il puntatore del mouse.
È stata inoltre aggiunta una videocamera di tipo FPS che consente di muoversi all'interno
della scena utilizzando il mouse e i tasti freccia.
NODO TERRENO
Il rendering di un terreno è implementato tramite un particolare tipo di nodo chiamato
ITerrainSceneNode.
Per visualizzare un terreno quindi, è necessario creare un nodo di questo tipo, impostarlo
correttamente con le opportune opzioni e quindi aggiungerlo alla scena che si sta
manipolando e che è gestita dallo SceneManager.
L'impostazione avviene specificando la HeightMap che sarà l’impronta del terreno, le
texture da applicare e gli eventuali ridimensionamenti che diano al tutto un aspetto
gradevole.
// aggiungo un nodo TerrainSceneNode
scene::ITerrainSceneNode* terrain = smgr->
addTerrainSceneNode("Media/terrain-heightmap.bmp");
// scalo la heightmap e disabilito l'illuminazione dinamica
terrain->setScale(core::vector3df(60, 4.4f, 60));
terrain->setMaterialFlag(video::EMF_LIGHTING, false);
// imposto la texture
terrain->setMaterialTexture(0, driver->getTexture("Media/terrain-texture.jpg"));
La creazione a run-time della mesh che rappresenta il terreno, con le opportune
ottimizzazioni, è possibile creare la mesh stessa al pari di qualsiasi altra mesh caricabile
da file.
Questo discorso vale anche per le diverse funzionalità applicabili a un corpo 3D.
Tra queste funzionalità c’è la rilevazione delle collisioni.
È possibile rilevare collisioni tra una mesh di terreno e un qualsiasi altro nodo di una scena
utilizzando un TriangleSelector e il relativo animatore (ISceneNodeAnimator).
Quest’ultimo mette in relazione l’oggetto su cui si concentra il gioco con l’oggetto col quale
si desidera conoscere le eventuali collisioni.
In questo esempio, l’oggetto collidente è il nodo videocamera creato prima: questo
consente di spostarsi sul terreno come se si camminasse sopra, senza oltrepassarlo.
// creo un TriangleSelector per la gestione delle collisioni
scene::ITriangleSelector* selector = smgr->
createTerrainTriangleSelector(terrain, 0);
terrain->setTriangleSelector(selector);
selector->drop();
// creo il corrispondente SceneNodeAnimator che risponda al contatto con la videocamera
scene::ISceneNodeAnimator* anim = smgr->
createCollisionResponseAnimator(selector, camera,
core::vector3df(60,100,60),core::vector3df(0,0,0),core::vector3df(0,50,0));
camera->addAnimator(anim);
IrrLicht
um 43 di 116
anim->drop();
L’applicazione è conclusa, non resta che scrivere il ciclo principale, dando un nome alla
finestra di esecuzione.
device->setWindowCaption(WINDOW_TITLE);
// titolo della finestra
while(device->run())
//ciclo principale
if (device->isWindowActive())
{
driver->beginScene(true, true, video::SColor(255,113,113,133));
smgr->drawAll();driver->endScene();
}
// rilascio le risorse e esco
device->drop();return (0);
}
// esempio: Terrain Rendering
#include <I:\irrlicht-1.6.1\irrlicht-1.6.1\include\irrlicht.h>
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
#define WINDOW_TITLE L"Esempio di Terrain Rendering"
int main(void)
{ // setup iniziali
IrrlichtDevice* device =
createDevice(video::EDT_OPENGL,core::dimension2d<u32>(640, 480),
16,false,false,false,0);
if (device == 0)
return 1; // impossibile creare il driver
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
// forzo la creazione di texture a 32 bit
driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, true);
// aggiungo e posiziono una videocamera controllabile coi tasti freccia
scene::ICameraSceneNode* camera = smgr>addCameraSceneNodeFPS(0,100.0f,1200.0f);
camera->setPosition(core::vector3df(1900*2,255*2,3700*2));
camera->setTarget(core::vector3df(2397*2,343*2,2700*2));
camera->setFarValue(12000.0f);
// disabilito il cursore del mouse
device->getCursorControl()->setVisible(false);
// aggiungo un nodo TerrainSceneNode
scene::ITerrainSceneNode* terrain = smgr->addTerrainSceneNode("Media/terrainheightmap.bmp");
// scalo la heightmap e disabilito l'illuminazione dinamica
IrrLicht
um 44 di 116
terrain->setScale(core::vector3df(40, 4.4f, 40));
terrain->setMaterialFlag(video::EMF_LIGHTING, false);
// imposto la texture
terrain->setMaterialTexture(0, driver->getTexture("Media/terrain-texture.jpg"));
// creo un TriangleSelector per la gestione delle collisioni
scene::ITriangleSelector* selector=smgr->createTerrainTriangleSelector(terrain, 0);
terrain->setTriangleSelector(selector);
selector->drop();
// creo il corrispondente SceneNodeAnimator che risponda al contatto con la
videocamera
scene::ISceneNodeAnimator* anim = smgr->createCollisionResponseAnimator(selector,
camera,core::vector3df(60,100,60),core::vector3df(0,0,0),core::vector3df(0,50,0));
camera->addAnimator(anim);
anim->drop();
// titolo della finestra
device->setWindowCaption(WINDOW_TITLE);
//ciclo principale
while(device->run())
if (device->isWindowActive())
{
driver->beginScene(true, true, video::SColor(255,113,113,133));
smgr->drawAll(); driver->endScene();
}
// rilascio le risorse e esco
device->drop(); return (0);
}
IrrLicht
um 45 di 116
MAPPA DEI DETTAGLI
La definizione del terreno è abbastanza soddisfacente, mentre le texture appaiono
alquanto deludenti.
Questo è dovuto al fatto che si è usato un fattore di scala 1.0, in altre parole non si è
scalato e la texture del terreno è stata spalmata su una mesh molto grande.
È possibile ovviare a questo inconveniente scalando di un fattore maggiore di 1.0 la
texture.
Questa, tuttavia, potrebbe non essere la soluzione migliore, in quanto la texture stessa
potrebbe essere legata in qualche modo alla HeightMap.
Ad esempio, potrebbe essere colorata di blu laddove ci si aspetta ci sia uno specchio
d’acqua.
Aumentando la scalatura si avrebbero degli effetti poco gradevoli alla vista e
sostanzialmente non corretti.
Un metodo migliore è quello di utilizzare una seconda mappa, chiamata mappa di dettagli
(Detail Map) che si sovrappone alla texture principale aggiungendo però dei dettagli.
Per utilizzare la Detail Map è sufficiente modificare parte del codice precedente.
// imposto texture principale e mappa dei detttagli
terrain->setMaterialTexture (0, driver->getTexture("Media/terrain-texture.jpg"));
terrain->setMaterialTexture (1, driver->getTexture("Media/detailmap3.jpg"));
terrain->setMaterialType(video::EMT_DETAIL_MAP);
// la texture principale e' inserita una sola volta
// la mappa dei dettagli è inserita 20 volte
terrain->scaleTexture(1.0f, 20.0f);
Invocando il metodo setMaterialTexture sulla posizione 1, oltre che sulla posizione 0, si
associa al nodo terrain un’altra texture.
Questa texture è considerata come una Detail Map ma solo dopo avere impostato il
parametro seguente.
video::EMT_DETAIL_MAP
Tramite l’invocazione di setMaterialType.
Successivamente, si effettua una scalatura tramite la scaleTexture, si mantiene a 1.0 il
fattore di scala della texture principale, mentre si ripete di un fattore 20.0 la texture dei
dettagli.
In questo modo questi saranno abbastanza precisi, anche quando si guarda molto da
vicino il terreno renderizzato.
// esempio: Terrain Rendering
#include <I:\irrlicht-1.6.1\irrlicht-1.6.1\include\irrlicht.h>
using namespace irr;
using namespace core;
using namespace scene;
IrrLicht
um 46 di 116
using namespace video;
using namespace io;
using namespace gui;
#define WINDOW_TITLE L"Esempio di Terrain Rendering"
int main(void)
{ // setup iniziali
IrrlichtDevice* device =
createDevice(video::EDT_OPENGL,core::dimension2d<u32>(640, 480),
16,false,false,false,0);
if (device == 0)
return 1; // impossibile creare il driver
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
// forzo la creazione di texture a 32 bit
driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, true);
// aggiungo e posiziono una videocamera controllabile coi tasti freccia
scene::ICameraSceneNode* camera = smgr->
addCameraSceneNodeFPS(0,100.0f,1200.0f);
camera->setPosition(core::vector3df(1900*2,255*2,3700*2));
camera->setTarget(core::vector3df(2397*2,343*2,2700*2));
camera->setFarValue(12000.0f);
// disabilito il cursore del mouse
device->getCursorControl()->setVisible(false);
// aggiungo un nodo TerrainSceneNode
scene::ITerrainSceneNode* terrain = smgr->
addTerrainSceneNode("Media/terrain-heightmap.bmp");
// scalo la heightmap e disabilito l'illuminazione dinamica
terrain->setScale(core::vector3df(60, 4.4f, 60));
terrain->setMaterialFlag(video::EMF_LIGHTING, false);
// imposto texture principale e mappa dei detttagli
terrain->setMaterialTexture (0, driver->getTexture("Media/terrain-texture.jpg"));
terrain->setMaterialTexture (1, driver->getTexture("Media/detailmap3.jpg"));
terrain->setMaterialType(video::EMT_DETAIL_MAP);
// la texture principale e' inserita una sola volta
// la mappa dei dettagli è inserita 20 volte
terrain->scaleTexture(1.0f, 20.0f);
// creo un TriangleSelector per la gestione delle collisioni
scene::ITriangleSelector* selector = smgr->
createTerrainTriangleSelector(terrain, 0);
terrain->setTriangleSelector(selector);
selector->drop();
// creo il corrispondente SceneNodeAnimator che risponda al contatto con la videocamera
scene::ISceneNodeAnimator* anim = smgr->
createCollisionResponseAnimator(selector, camera,
core::vector3df(60,100,60),core::vector3df(0,0,0),core::vector3df(0,50,0));
camera->addAnimator(anim);
anim->drop();
// titolo della finestra
device->setWindowCaption(WINDOW_TITLE);
//ciclo principale
while(device->run())
if (device->isWindowActive())
{
driver->beginScene(true, true, 0);
smgr->drawAll();
IrrLicht
um 47 di 116
driver->endScene();
}
// rilascio le risorse e esco
device->drop();
return (0);
}
Adesso bisogna aggiungere la possibilità, da parte dell’utente, di modificare a run-time
l’utilizzo o meno della Detail Map.
Questo effetto è ottenuto impostando alternativamente i valori seguenti.
video::EMT_DETAIL_MAP e video::EMT_SOLID
Per il terreno tramite la setMaterialType.
Per catturare la pressione del tasto che permette di effettuare questo cambiamento, si è
scelto lo SPAZIO, si deve derivare una classe dalla classe IeventReceiver.
Il codice di questa classe è il seguente.
// l'EventReceiver che consente di visualizzare o meno i dettagli
class MyEventReceiver : public IEventReceiver
{ public:
MyEventReceiver(scene::ISceneNode* terrain)
{
// puntatore al nodo utilizzato
Terrain = terrain;
}
IrrLicht
um 48 di 116
virtual bool OnEvent(const SEvent& event)
{
// check per la presisone del tasto SPAZIO
if (event.EventType == irr::EET_KEY_INPUT_EVENT &&
!event.KeyInput.PressedDown)
{ switch (event.KeyInput.Key)
{ // attiva/disattiva la detail map
case irr::KEY_SPACE:
Terrain->setMaterialType (Terrain->getMaterial(0).MaterialType
== video::EMT_SOLID ? video::EMT_DETAIL_MAP:
video::EMT_SOLID);
return true;
}
}
return false;
}
private:
scene::ISceneNode* Terrain;
};
Come si può notare, nel caso il tasto premuto e rilasciato sia il seguente.
irr::KEY_SPACE
In pratica, lo SPAZIO, l’EventReceiver si occupa di effettuare il cambiamento tra i
parametri visti in precedenza.
Per utilizzare MyEventReceiver è necessario inoltre aggiungere all’interno della funzione
main il seguente codice.
// istanzio un oggetto MyEventReceiver in modo da poter abilitare la mappa di dettagli
MyEventReceiver receiver(terrain);
device->setEventReceiver(&receiver);
// esempio: Terrain Rendering
#include <I:\irrlicht-1.6.1\irrlicht-1.6.1\include\irrlicht.h>
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
#define WINDOW_TITLE L"Esempio di Terrain Rendering"
// l'EventReceiver che consente di visualizzare o meno i dettagli
class MyEventReceiver : public IEventReceiver
{ public:
MyEventReceiver(scene::ISceneNode* terrain)
{
// puntatore al nodo utilizzato
Terrain = terrain;
}
virtual bool OnEvent(const SEvent& event)
{
// check per la presisone del tasto SPAZIO
if (event.EventType == irr::EET_KEY_INPUT_EVENT &&
!event.KeyInput.PressedDown)
{ switch (event.KeyInput.Key)
{ // attiva/disattiva la detail map
IrrLicht
um 49 di 116
case irr::KEY_SPACE:
Terrain->setMaterialType (Terrain->getMaterial(0).MaterialType
== video::EMT_SOLID ? video::EMT_DETAIL_MAP:
video::EMT_SOLID);
return true;
}
}
return false;
}
private:
scene::ISceneNode* Terrain;
};
int main(void)
{
// setup iniziali
IrrlichtDevice* device =
createDevice(video::EDT_OPENGL,core::dimension2d<u32>(640, 480),
16,false,false,false,0);
if (device == 0)
return 1; // impossibile creare il driver
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
// forzo la creazione di texture a 32 bit
driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, true);
// aggiungo e posiziono una videocamera controllabile coi tasti freccia
scene::ICameraSceneNode* camera = smgr->
addCameraSceneNodeFPS(0,100.0f,1200.0f);
camera->setPosition(core::vector3df(1900*2,255*2,3700*2));
camera->setTarget(core::vector3df(2397*2,343*2,2700*2));
camera->setFarValue(12000.0f);
// disabilito il cursore del mouse
device->getCursorControl()->setVisible(false);
// aggiungo un nodo TerrainSceneNode
scene::ITerrainSceneNode* terrain = smgr->
addTerrainSceneNode("Media/terrain-heightmap.bmp");
// scalo la heightmap e disabilito l'illuminazione dinamica
terrain->setScale(core::vector3df(60, 4.4f, 60));
terrain->setMaterialFlag(video::EMF_LIGHTING, false);
// istanzio un oggetto MyEventReceiver in modo da poter abilitare la mappa di dettagli
MyEventReceiver receiver(terrain);
device->setEventReceiver(&receiver);
// imposto texture principale e mappa dei detttagli
terrain->setMaterialTexture (0, driver->getTexture("Media/terrain-texture.jpg"));
terrain->setMaterialTexture (1, driver->getTexture("Media/detailmap3.jpg"));
terrain->setMaterialType(video::EMT_DETAIL_MAP);
// La texture principale e' inserita una sola volta
// La mappa dei dettagli è inserita 20 volte
terrain->scaleTexture(1.0f, 20.0f);
// creo un TriangleSelector per la gestione delle collisioni
scene::ITriangleSelector* selector = smgr->
createTerrainTriangleSelector(terrain, 0);
terrain->setTriangleSelector(selector);
selector->drop();
// creo il corrispondente SceneNodeAnimator che risponda al contatto con la videocamera
scene::ISceneNodeAnimator* anim = smgr->
IrrLicht
um 50 di 116
createCollisionResponseAnimator(selector, camera,
core::vector3df(60,100,60),core::vector3df(0,0,0),core::vector3df(0,50,0));
camera->addAnimator(anim);
anim->drop();
// titolo della finestra
device->setWindowCaption(WINDOW_TITLE);
//ciclo principale
while(device->run())
if (device->isWindowActive())
{
driver->beginScene(true, true, 0);
smgr->drawAll();
driver->endScene();
}
// rilascio le risorse e esco
device->drop();
return (0);
}
IrrLicht
um 51 di 116
TERRAIN RENDERING
INTRODUZIONE
Nel progetto Visual Studio ci sono i seguenti file.
All’interno della cartella del progetto creare le sotto cartelle LIBRERIE e MEDIA.
File MAIN.CPP
#include <I:\irrlicht-1.6.1\irrlicht-1.6.1\include\irrlicht.h>
#include <iostream>
using namespace irr;
using namespace video;
using namespace scene;
using namespace core;
using namespace video;
using namespace io;
using namespace gui;
class MyEventReceiver : public IEventReceiver
{ public:
MyEventReceiver(scene::ISceneNode* terrain, scene::ISceneNode* skybox,
scene::ISceneNode* skydome) :
Terrain(terrain), Skybox(skybox), Skydome(skydome), showBox(true)
{
Skybox->setVisible(true);
Skydome->setVisible(false);
}
bool OnEvent(const SEvent& event)
{
// controllo se l'utente ha premuto 'W' or 'D'
if (event.EventType == irr::EET_KEY_INPUT_EVENT &&
!event.KeyInput.PressedDown)
{
switch (event.KeyInput.Key)
{
case irr::KEY_KEY_W: // switch wire frame mode
IrrLicht
um 52 di 116
Terrain->setMaterialFlag(video::EMF_WIREFRAME,
!Terrain->getMaterial(0).Wireframe);
Terrain->setMaterialFlag(video::EMF_POINTCLOUD, false);
return true;
case irr::KEY_KEY_P: // switch wire frame mode
Terrain->setMaterialFlag(video::EMF_POINTCLOUD,
!Terrain->getMaterial(0).PointCloud);
Terrain->setMaterialFlag(video::EMF_WIREFRAME, false);
return true;
case irr::KEY_KEY_D: // detail map
Terrain->setMaterialType(
Terrain->getMaterial(0).MaterialType ==
video::EMT_SOLID ?
video::EMT_DETAIL_MAP : video::EMT_SOLID);
return true;
case irr::KEY_KEY_S: // skybox/skydome
showBox=!showBox;
Skybox->setVisible(showBox);
Skydome->setVisible(!showBox);
return true;
default:
break;
}
}
return false;
}
private:
scene::ISceneNode* Terrain;
scene::ISceneNode* Skybox;
scene::ISceneNode* Skydome;
bool showBox;
};
int main(void)
{
// setup iniziali
IrrlichtDevice *device =
createDevice(EDT_DIRECT3D9, dimension2d<u32>(640, 480), 16, false);
if (device == 0) return 1; // impossibile creare il driver
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
gui::IGUIEnvironment* env = device->getGUIEnvironment();
driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, true);
// aggiungo il logo
env->addImage(driver->getTexture("Media/42.jpg"),
core::position2d<s32>(10,10));
// seleziono il font
env->getSkin()->setFont(env->getFont("Media/fontlucida.png"));
// aggiungo il testo
env->addStaticText(L"Premi 'W' per il wireframe mode\nPremi 'P' per il pointcloud
mode\nPremi 'D' per lo switch detail map\nPremi 'S' per lo switch skybox/skydome",
core::rect<s32>(10,400,270,475), true, true, 0, -1, true);
scene::ICameraSceneNode* camera =
smgr->addCameraSceneNodeFPS(0,100.0f,1.2f);
camera->setPosition(core::vector3df(2700*2,255*2,2600*2));
camera->setTarget(core::vector3df(2397*2,343*2,2700*2));
IrrLicht
um 53 di 116
camera->setFarValue(42000.0f);
device->getCursorControl()->setVisible(false);
// aggiungo il terreno alla scene node
scene::ITerrainSceneNode* terrain = smgr->addTerrainSceneNode(
"Media/terrain-heightmap.bmp",
0,
// parent node
-1,
// node id
core::vector3df(0.f, 0.f, 0.f),
// position
core::vector3df(0.f, 0.f, 0.f),
// rotation
core::vector3df(40.f, 4.4f, 40.f), // scale
video::SColor ( 255, 255, 255, 255 ),
// vertexColor
5,
// maxLOD
scene::ETPS_17,
// patchSize
4
// smoothFactor
);
terrain->setMaterialFlag(video::EMF_LIGHTING, false);
terrain->setMaterialTexture(0,
driver->getTexture("Media/terrain-texture.jpg"));
terrain->setMaterialTexture(1,
driver->getTexture("Media/detailmap3.jpg"));
terrain->setMaterialType(video::EMT_DETAIL_MAP);
terrain->scaleTexture(1.0f, 20.0f);
// creo il triangolo selector per il terreno
scene::ITriangleSelector* selector
= smgr->createTerrainTriangleSelector(terrain, 0);
terrain->setTriangleSelector(selector);
// creo il gestore delle collisioni
scene::ISceneNodeAnimator* anim = smgr->createCollisionResponseAnimator(
selector, camera, core::vector3df(60,100,60),
core::vector3df(0,0,0),
core::vector3df(0,50,0));
selector->drop();
camera->addAnimator(anim);
anim->drop();
scene::CDynamicMeshBuffer* buffer =
new scene::CDynamicMeshBuffer(video::EVT_2TCOORDS,
video::EIT_16BIT);
terrain->getMeshBufferForLOD(*buffer, 0);
video::S3DVertex2TCoords* data = (video::S3DVertex2TCoords*)buffer->
getVertexBuffer().getData();
buffer->drop();
// creo skybox e skydome
driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
scene::ISceneNode* skybox=smgr->addSkyBoxSceneNode(
driver->getTexture("Media/irrlicht2_up.jpg"),
driver->getTexture("Media/irrlicht2_dn.jpg"),
driver->getTexture("Media/irrlicht2_lf.jpg"),
driver->getTexture("Media/irrlicht2_rt.jpg"),
driver->getTexture("Media/irrlicht2_ft.jpg"),
driver->getTexture("Media/irrlicht2_bk.jpg"));
scene::ISceneNode* skydome=smgr->addSkyDomeSceneNode(driver->
getTexture("Media/skydome.jpg"),16,8,0.95f,2.0f);
driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true);
// creo l'event receiver
IrrLicht
um 54 di 116
MyEventReceiver receiver(terrain, skybox, skydome);
device->setEventReceiver(&receiver);
int lastFPS = -1;
while(device->run())
if (device->isWindowActive())
{
driver->beginScene(true, true, 0 );
smgr->drawAll();
env->drawAll();
driver->endScene();
int fps = driver->getFPS();
if (lastFPS != fps)
{
core::stringw str = L"Terrain Renderer. Esempio [";
str += driver->getName();
str += "] FPS:";
str += fps;
str += " Height: ";
str += terrain->getHeight(camera->getAbsolutePosition().X,
camera->getAbsolutePosition().Z);
device->setWindowCaption(str.c_str());
lastFPS = fps;
}
}
device->drop();return 0;
}
IrrLicht
um 55 di 116
TETRAEDRO
INTRODUZIONE
Nel progetto Visual Studio ci sono i seguenti file.
All’interno della cartella del progetto creare la sotto cartella LIBRERIE.
File MAIN.CPP
#include <I:\irrlicht-1.6.1\irrlicht-1.6.1\include\irrlicht.h>
#include <iostream>
using namespace irr;
using namespace video;
using namespace scene;
using namespace core;
using namespace video;
using namespace io;
using namespace gui;
class CSampleSceneNode : public scene::ISceneNode
{
/* dichiaro alcuni membri delle variabili:
il bounding box, 4 vertici, e il materiale del tetraedro*/
core::aabbox3d<f32> Box;
video::S3DVertex Vertices[4];
video::SMaterial Material;
public:
CSampleSceneNode(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id)
: scene::ISceneNode(parent, mgr, id)
{
Material.Wireframe = false;
Material.Lighting = false;
Vertices[0] = video::S3DVertex(0,0,10, 1,1,0,video::SColor(255,0,255,255), 0, 1);
Vertices[1] = video::S3DVertex(10,0,-10, 1,0,0,video::SColor(255,255,0,255), 1, 1);
Vertices[2] = video::S3DVertex(0,20,0, 0,1,1,video::SColor(255,255,255,0), 1, 0);
Vertices[3] = video::S3DVertex(-10,0,-10, 0,0,1,video::SColor(255,0,255,0), 0, 0);
Box.reset(Vertices[0].Pos);
for (s32 i=1; i<4; ++i)
Box.addInternalPoint(Vertices[i].Pos);
}
virtual void OnRegisterSceneNode()
{
if (IsVisible)SceneManager->registerNodeForRendering(this);
ISceneNode::OnRegisterSceneNode();
}
virtual void render()
{
u16 indices[] = {
0,2,3, 2,1,3, 1,0,3, 2,0,1 };
video::IVideoDriver* driver = SceneManager->getVideoDriver();
driver->setMaterial(Material);
IrrLicht
um 56 di 116
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
driver->drawIndexedTriangleList(&Vertices[0], 4, &indices[0], 4);
}
virtual const core::aabbox3d<f32>& getBoundingBox() const
{ return Box;}
virtual u32 getMaterialCount() const
{ return 1;}
virtual video::SMaterial& getMaterial(u32 i)
{ return Material;}
};
int main(void)
{
// setup iniziali
IrrlichtDevice *device =
createDevice(EDT_DIRECT3D9, dimension2d<u32>(640, 480), 16, false);
if (device == 0) return 1; // impossibile creare il driver
// creazione della videocamera
device->setWindowCaption(L"Custom Scene Node");
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
smgr->addCameraSceneNode(0, core::vector3df(0,-40,0), core::vector3df(0,0,0));
/* crea scene node */
CSampleSceneNode *myNode =new CSampleSceneNode(smgr->
getRootSceneNode(), smgr, 666);
scene::ISceneNodeAnimator* anim =smgr->
createRotationAnimator(core::vector3df(0.8f, 0, 0.8f));
if(anim)
{
myNode->addAnimator(anim);
anim->drop();
anim = 0;
}
myNode->drop();
myNode = 0;
u32 frames=0;
while(device->run())
{
driver->beginScene(true, true, video::SColor(0,100,100,100));
smgr->drawAll();
driver->endScene();
if (++frames==100)
{
core::stringw str = L"Irrlicht Engine [";
str += driver->getName();
str += L"] FPS: ";
str += (s32)driver->getFPS();
device->setWindowCaption(str.c_str());
frames=0;
}
}
device->drop();
return (0);
}
IrrLicht
um 57 di 116
IrrLicht
um 58 di 116
FILE
INTRODUZIONE
Nel progetto Visual Studio ci sono i seguenti file.
All’interno della cartella del progetto creare le sotto cartelle LIBRERIE e MEDIA.
File ESEMPIO.IRR
IrrLicht salva e carica la scena in un file formato XML (eXtensible Markup Language).
L’editor irrEDit per editare file di questo tipo è disponibile all’indirizzo.
http://www.ambiera.com/irredit
<?xml version="1.0"?>
<irr_scene>
<attributes>
<string name="Name" value="root" />
<int name="Id" value="-1" />
<vector3d name="Position" value="0.000000, 0.000000, 0.000000" />
<vector3d name="Rotation" value="0.000000, 0.000000, 0.000000" />
<vector3d name="Scale" value="1.000000, 1.000000, 1.000000" />
<bool name="Visible" value="true" />
<enum name="AutomaticCulling" value="box" />
<bool name="DebugDataVisible" value="false" />
<bool name="IsDebugObject" value="false" />
</attributes>
<userData>
<attributes>
<bool name="OccludesLight" value="false" />
</attributes>
</userData>
<node type="mesh">
<attributes>
<string name="Name" value="" />
<int name="Id" value="-1" />
<vector3d name="Position" value="3.048694, -16.834227, -3.333332" />
<vector3d name="Rotation" value="0.000000, 0.000000, 0.000000" />
IrrLicht
um 59 di 116
<vector3d name="Scale" value="0.200000, 0.200000, 0.200000" />
<bool name="Visible" value="true" />
<enum name="AutomaticCulling" value="box" />
<bool name="DebugDataVisible" value="false" />
<bool name="IsDebugObject" value="false" />
<string name="Mesh" value="Media/room.3ds" />
<bool name="ReadOnlyMaterials" value="false" />
</attributes>
<materials>
<attributes>
<enum name="Type" value="solid" />
<colorf name="Ambient" value="1.000000, 1.000000, 1.000000, 1.000000" />
<colorf name="Diffuse" value="1.000000, 1.000000, 1.000000, 1.000000" />
<colorf name="Emissive" value="0.000000, 0.000000, 0.000000, 0.000000" />
<colorf name="Specular" value="1.000000, 1.000000, 1.000000, 1.000000" />
<float name="Shininess" value="0.000000" />
<float name="Param1" value="0.000000" />
<float name="Param2" value="0.000000" />
<texture name="Texture1" value="Media/wall.jpg" />
<texture name="Texture2" value="" />
<texture name="Texture3" value="" />
<texture name="Texture4" value="" />
<bool name="Wireframe" value="false" />
<bool name="GouraudShading" value="true" />
<bool name="Lighting" value="true" />
<bool name="ZBuffer" value="true" />
<bool name="ZWriteEnable" value="true" />
<bool name="BackfaceCulling" value="true" />
<bool name="BilinearFilter" value="true" />
<bool name="TrilinearFilter" value="false" />
<bool name="AnisotropicFilter" value="false" />
<bool name="FogEnable" value="false" />
<bool name="NormalizeNormals" value="true" />
</attributes>
</materials>
<userData>
<attributes>
<bool name="ReceivesShadows" value="true" />
<bool name="OccludesLight" value="true" />
</attributes>
</userData>
</node>
<node type="light">
<attributes>
<string name="Name" value="" />
<int name="Id" value="-1" />
<vector3d name="Position" value="27.366207, 20.000000, 29.173460" />
<vector3d name="Rotation" value="49.740002, 0.000000, 0.000000" />
<vector3d name="Scale" value="1.000000, 1.000000, 1.000000" />
<bool name="Visible" value="true" />
<enum name="AutomaticCulling" value="false" />
<bool name="DebugDataVisible" value="false" />
<bool name="IsDebugObject" value="false" />
<colorf name="AmbientColor" value="0.000000, 0.000000, 0.000000, 1.000000" />
IrrLicht
um 60 di 116
<colorf name="DiffuseColor" value="1.000000, 0.501961, 0.000000, 1.000000" />
<colorf name="SpecularColor" value="1.000000, 1.000000, 1.000000, 1.000000" />
<float name="Radius" value="1000.000000" />
<bool name="CastShadows" value="true" />
<enum name="LightType" value="Point" />
</attributes>
<animators>
<attributes>
<string name="Type" value="flyCircle" />
<vector3d name="Center" value="0.000000, 20.000000, 0.000000" />
<float name="Radius" value="40.000000" />
<float name="Speed" value="0.001000" />
</attributes>
</animators>
<userData>
<attributes>
<bool name="OccludesLight" value="false" />
</attributes>
</userData>
<node type="billBoard">
<attributes>
<string name="Name" value="" />
<int name="Id" value="-1" />
<vector3d name="Position" value="0.000000, 0.000000, 0.000000" />
<vector3d name="Rotation" value="0.000000, 0.000000, 0.000000" />
<vector3d name="Scale" value="1.000000, 1.000000, 1.000000" />
<bool name="Visible" value="true" />
<enum name="AutomaticCulling" value="box" />
<bool name="DebugDataVisible" value="false" />
<bool name="IsDebugObject" value="false" />
<float name="Width" value="30.000000" />
<float name="Height" value="30.000000" />
</attributes>
<materials>
<attributes>
<enum name="Type" value="trans_add" />
<colorf name="Ambient" value="1.000000, 1.000000, 1.000000, 1.000000" />
<colorf name="Diffuse" value="1.000000, 1.000000, 1.000000, 1.000000" />
<colorf name="Emissive" value="0.000000, 0.000000, 0.000000, 0.000000" />
<colorf name="Specular" value="1.000000, 1.000000, 1.000000, 1.000000" />
<float name="Shininess" value="0.000000" />
<float name="Param1" value="0.000000" />
<float name="Param2" value="0.000000" />
<texture name="Texture1" value="Media/particlewhite.bmp" />
<texture name="Texture2" value="" />
<texture name="Texture3" value="" />
<texture name="Texture4" value="" />
<bool name="Wireframe" value="false" />
<bool name="GouraudShading" value="true" />
<bool name="Lighting" value="false" />
<bool name="ZBuffer" value="true" />
<bool name="ZWriteEnable" value="true" />
<bool name="BackfaceCulling" value="false" />
<bool name="BilinearFilter" value="true" />
IrrLicht
um 61 di 116
<bool name="TrilinearFilter" value="false" />
<bool name="AnisotropicFilter" value="false" />
<bool name="FogEnable" value="false" />
<bool name="NormalizeNormals" value="false" />
</attributes>
</materials>
<userData>
<attributes>
<bool name="OccludesLight" value="false" />
</attributes>
</userData>
</node>
</node>
<node type="billBoard">
<attributes>
<string name="Name" value="" />
<int name="Id" value="-1" />
<vector3d name="Position" value="111.489021, 235.172729, -124.924538" />
<vector3d name="Rotation" value="0.000000, 0.000000, 0.000000" />
<vector3d name="Scale" value="1.000000, 1.000000, 1.000000" />
<bool name="Visible" value="true" />
<bool name="AutomaticCulling" value="box" />
<bool name="DebugDataVisible" value="false" />
<bool name="IsDebugObject" value="false" />
<float name="Width" value="10.000000" />
<float name="Height" value="10.000000" />
</attributes>
<materials>
<attributes>
<enum name="Type" value="trans_alphach" />
<colorf name="Ambient" value="1.000000, 1.000000, 1.000000, 1.000000" />
<colorf name="Diffuse" value="1.000000, 1.000000, 1.000000, 1.000000" />
<colorf name="Emissive" value="0.000000, 0.000000, 0.000000, 0.000000" />
<colorf name="Specular" value="1.000000, 1.000000, 1.000000, 1.000000" />
<float name="Shininess" value="0.000000" />
<float name="Param1" value="0.000000" />
<float name="Param2" value="0.000000" />
<texture name="Texture1" value="Media/particlewhite.bmp" />
<texture name="Texture2" value="" />
<texture name="Texture3" value="" />
<texture name="Texture4" value="" />
<bool name="Wireframe" value="false" />
<bool name="GouraudShading" value="true" />
<bool name="Lighting" value="false" />
<bool name="ZBuffer" value="true" />
<bool name="ZWriteEnable" value="true" />
<bool name="BackfaceCulling" value="false" />
<bool name="BilinearFilter" value="true" />
<bool name="TrilinearFilter" value="false" />
<bool name="AnisotropicFilter" value="false" />
<bool name="FogEnable" value="false" />
<bool name="NormalizeNormals" value="false" />
</attributes>
</materials>
IrrLicht
um 62 di 116
<userData>
<attributes>
<bool name="OccludesLight" value="false" />
</attributes>
</userData>
</node>
<node type="cube">
<attributes>
<string name="Name" value="" />
<int name="Id" value="-1" />
<vector3d name="Position" value="-12.696126, 13.808047, 7.329002" />
<vector3d name="Rotation" value="39445.453125, 25926.058594, 0.000000" />
<vector3d name="Scale" value="1.000000, 1.000000, 1.000000" />
<bool name="Visible" value="true" />
<bool name="AutomaticCulling" value="box" />
<bool name="DebugDataVisible" value="false" />
<bool name="IsDebugObject" value="false" />
<float name="Size" value="10.000000" />
</attributes>
<materials>
<attributes>
<enum name="Type" value="solid" />
<colorf name="Ambient" value="1.000000, 1.000000, 1.000000, 1.000000" />
<colorf name="Diffuse" value="1.000000, 1.000000, 1.000000, 1.000000" />
<colorf name="Emissive" value="0.000000, 0.000000, 0.000000, 0.000000" />
<colorf name="Specular" value="1.000000, 1.000000, 1.000000, 1.000000" />
<float name="Shininess" value="0.000000" />
<float name="Param1" value="0.000000" />
<float name="Param2" value="0.000000" />
<texture name="Texture1" value="Media/rockwall.jpg" />
<texture name="Texture2" value="" />
<texture name="Texture3" value="" />
<texture name="Texture4" value="" />
<bool name="Wireframe" value="false" />
<bool name="GouraudShading" value="true" />
<bool name="Lighting" value="true" />
<bool name="ZBuffer" value="true" />
<bool name="ZWriteEnable" value="true" />
<bool name="BackfaceCulling" value="true" />
<bool name="BilinearFilter" value="true" />
<bool name="TrilinearFilter" value="false" />
<bool name="AnisotropicFilter" value="false" />
<bool name="FogEnable" value="false" />
<bool name="NormalizeNormals" value="false" />
</attributes>
</materials>
<animators>
<attributes>
<string name="Type" value="rotation" />
<vector3d name="Rotation" value="0.400000, 0.300000, 0.000000" />
</attributes>
</animators>
<userData>
<attributes>
IrrLicht
um 63 di 116
<bool name="OccludesLight" value="false" />
</attributes>
</userData>
<node type="sphere">
<attributes>
<string name="Name" value="" />
<int name="Id" value="-1" />
<vector3d name="Position" value="9.000000, 9.000000, 9.000000" />
<vector3d name="Rotation" value="88082.054688, 87970.695313, 87970.695313" />
<vector3d name="Scale" value="1.000000, 1.000000, 1.000000" />
<bool name="Visible" value="true" />
<enum name="AutomaticCulling" value="box" />
<bool name="DebugDataVisible" value="false" />
<bool name="IsDebugObject" value="false" />
<float name="Radius" value="5.000000" />
<int name="PolyCount" value="16" />
</attributes>
<materials>
<attributes>
<enum name="Type" value="solid" />
<colorf name="Ambient" value="1.000000, 1.000000, 1.000000, 1.000000" />
<colorf name="Diffuse" value="1.000000, 1.000000, 1.000000, 1.000000" />
<colorf name="Emissive" value="0.000000, 0.000000, 0.000000, 0.000000" />
<colorf name="Specular" value="1.000000, 1.000000, 1.000000, 1.000000" />
<float name="Shininess" value="0.000000" />
<float name="Param1" value="0.000000" />
<float name="Param2" value="0.000000" />
<texture name="Texture1" value="Media/rockwall.jpg" />
<texture name="Texture2" value="" />
<texture name="Texture3" value="" />
<texture name="Texture4" value="" />
<bool name="Wireframe" value="false" />
<bool name="GouraudShading" value="true" />
<bool name="Lighting" value="true" />
<bool name="ZBuffer" value="true" />
<bool name="ZWriteEnable" value="true" />
<bool name="BackfaceCulling" value="true" />
<bool name="BilinearFilter" value="true" />
<bool name="TrilinearFilter" value="false" />
<bool name="AnisotropicFilter" value="false" />
<bool name="FogEnable" value="false" />
<bool name="NormalizeNormals" value="false" />
</attributes>
</materials>
<animators>
<attributes>
<string name="Type" value="rotation" />
<vector3d name="Rotation" value="1.000000, 1.000000, 1.000000" />
</attributes>
</animators>
<userData>
<attributes>
<bool name="OccludesLight" value="false" />
</attributes>
IrrLicht
um 64 di 116
</userData>
</node>
</node>
<node type="particleSystem">
<attributes>
<string name="Name" value="" />
<int name="Id" value="-1" />
<vector3d name="Position" value="-12.434986, -1.698202, 10.594842" />
<vector3d name="Rotation" value="0.000000, 0.000000, 0.000000" />
<vector3d name="Scale" value="1.000000, 1.000000, 1.000000" />
<bool name="Visible" value="true" />
<enum name="AutomaticCulling" value="box" />
<bool name="DebugDataVisible" value="false" />
<bool name="IsDebugObject" value="false" />
<bool name="GlobalParticles" value="false" />
<float name="ParticleWidth" value="2.000000" />
<float name="ParticleHeight" value="2.000000" />
<enum name="Emitter" value="Box" />
<vector3d name="Box" value="2.000000, 2.000000, 2.000000" />
<vector3d name="Direction" value="0.000000, 0.030000, 0.000000" />
<int name="MinParticlesPerSecond" value="20" />
<int name="MaxParticlesPerSecond" value="60" />
<color name="MinStartColor" value="ffa6ec13" />
<color name="MaxStartColor" value="ffe0a01d" />
<int name="MinLifeTime" value="4000" />
<int name="MaxLifeTime" value="5000" />
<int name="MaxAngleDegrees" value="30" />
<enum name="Affector" value="Gravity" />
<vector3d name="Gravity" value="0.000000, -0.030000, 0.000000" />
<float name="TimeForceLost" value="5000.000000" />
</attributes>
<materials>
<attributes>
<enum name="Type" value="trans_add" />
<colorf name="Ambient" value="1.000000, 1.000000, 1.000000, 1.000000" />
<colorf name="Diffuse" value="1.000000, 1.000000, 1.000000, 1.000000" />
<colorf name="Emissive" value="0.000000, 0.000000, 0.000000, 0.000000" />
<colorf name="Specular" value="1.000000, 1.000000, 1.000000, 1.000000" />
<float name="Shininess" value="0.000000" />
<float name="Param1" value="0.000000" />
<float name="Param2" value="0.000000" />
<texture name="Texture1" value="Media/particlered.bmp" />
<texture name="Texture2" value="" />
<texture name="Texture3" value="" />
<texture name="Texture4" value="" />
<bool name="Wireframe" value="false" />
<bool name="GouraudShading" value="true" />
<bool name="Lighting" value="false" />
<bool name="ZBuffer" value="true" />
<bool name="ZWriteEnable" value="false" />
<bool name="BackfaceCulling" value="false" />
<bool name="BilinearFilter" value="true" />
<bool name="TrilinearFilter" value="false" />
<bool name="AnisotropicFilter" value="false" />
IrrLicht
um 65 di 116
<bool name="FogEnable" value="false" />
<bool name="NormalizeNormals" value="false" />
</attributes>
</materials>
<userData>
<attributes>
<bool name="OccludesLight" value="false" />
</attributes>
</userData>
</node>
</irr_scene>
File MAIN.CPP
#include <I:\irrlicht-1.6.1\irrlicht-1.6.1\include\irrlicht.h>
#include <iostream>
using namespace irr;
using namespace video;
using namespace scene;
using namespace core;
using namespace video;
using namespace io;
using namespace gui;
int main(int argc, char** argv)
{
// setup iniziali
IrrlichtDevice *device =
createDevice(EDT_DIRECT3D9, dimension2d<u32>(640, 480), 16, false);
if (device == 0) return 1; // impossibile creare il driver
device->setWindowCaption(L"Carico il file *.irr");
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
// carico la scena
if (argc>1) smgr->loadScene(argv[1]);
else smgr->loadScene("Media/esempio.irr");
scene::ICameraSceneNode * camera = smgr->addCameraSceneNodeFPS(0, 50.f, 0.1f);
scene::IMetaTriangleSelector * meta = smgr->createMetaTriangleSelector();
core::array<scene::ISceneNode *> nodes;
smgr->getSceneNodesFromType(scene::ESNT_ANY, nodes);
for (u32 i=0; i < nodes.size(); ++i)
{
scene::ISceneNode * node = nodes[i];
scene::ITriangleSelector * selector = 0;
switch(node->getType())
{
case scene::ESNT_CUBE:
case scene::ESNT_ANIMATED_MESH:
selector = smgr->createTriangleSelectorFromBoundingBox(node);
break;
case scene::ESNT_MESH:
case scene::ESNT_SPHERE:
selector = smgr->
createTriangleSelector(((scene::IMeshSceneNode*)node)->getMesh(), node);
break;
case scene::ESNT_TERRAIN:
selector = smgr->
createTerrainTriangleSelector((scene::ITerrainSceneNode*)node);
IrrLicht
um 66 di 116
break;
case scene::ESNT_OCT_TREE:
selector = smgr->
createOctTreeTriangleSelector(((scene::IMeshSceneNode*)node)->getMesh(), node);
break;
default:
break;
}
if(selector)
{
meta->addTriangleSelector(selector);
selector->drop();
}
}
scene::ISceneNodeAnimator* anim = smgr->createCollisionResponseAnimator(
meta, camera, core::vector3df(5,5,5),
core::vector3df(0,0,0));
meta->drop();
camera->addAnimator(anim);
anim->drop();
camera->setPosition(core::vector3df(0.f, 20.f, 0.f));
scene::ISceneNode * cube = smgr->
getSceneNodeFromType(scene::ESNT_CUBE);
if(cube)camera->setTarget(cube->getAbsolutePosition());
int lastFPS = -1;
while(device->run())
if (device->isWindowActive())
{
driver->beginScene(true, true, video::SColor(0,200,200,200));
smgr->drawAll();
driver->endScene();
int fps = driver->getFPS();
if (lastFPS != fps)
{
core::stringw str = L"File *.irr. Esempio [";
str += driver->getName();
str += "] FPS:";
str += fps;
device->setWindowCaption(str.c_str());
lastFPS = fps;
}
}
device->drop();
return 0;
}
IrrLicht
um 67 di 116
IrrLicht
um 68 di 116
UI (USER INTERFACE)
INTRODUZIONE
Nel progetto Visual Studio ci sono i seguenti file.
All’interno della cartella del progetto creare le sotto cartelle LIBRERIE e MEDIA.
File MAIN.CPP
#include <I:\irrlicht-1.6.1\irrlicht-1.6.1\include\irrlicht.h>
#include <iostream>
using namespace irr;
using namespace video;
using namespace scene;
using namespace core;
using namespace video;
using namespace io;
using namespace gui;
// dichiarazione del record
struct SAppContext
{
IrrlichtDevice *device;
s32
counter;
IGUIListBox* listbox;
};
// definizione dei controlli GUI
enum
{
GUI_ID_QUIT_BUTTON = 101,
GUI_ID_NEW_WINDOW_BUTTON,
GUI_ID_FILE_OPEN_BUTTON,
GUI_ID_TRANSPARENCY_SCROLL_BAR
};
class MyEventReceiver : public IEventReceiver
{ public:
MyEventReceiver(SAppContext & context) : Context(context) { }
virtual bool OnEvent(const SEvent& event)
{
if (event.EventType == EET_GUI_EVENT)
{
s32 id = event.GUIEvent.Caller->getID();
IGUIEnvironment* env = Context.device->getGUIEnvironment();
switch(event.GUIEvent.EventType)
{// gestione della scrollbar
case EGET_SCROLL_BAR_CHANGED:
IrrLicht
um 69 di 116
{
if (id == GUI_ID_TRANSPARENCY_SCROLL_BAR)
{
s32 pos = ((IGUIScrollBar*)event.GUIEvent.Caller)->getPos();
for (u32 i=0; i<EGDC_COUNT ; ++i)
SColor col = env->getSkin()->getColor((EGUI_DEFAULT_COLOR)i);
col.setAlpha(pos);
env->getSkin()->setColor((EGUI_DEFAULT_COLOR)i, col);
}
}
break;
/* se il bottone è premuto si vedono 3 bottoni
primo: si scegli la grafica
secondo: crea una window con il testo
terzo: crea una finestra di dialogo */
case EGET_BUTTON_CLICKED:
switch(id)
{
case GUI_ID_QUIT_BUTTON:
Context.device->closeDevice();
return true;
case GUI_ID_NEW_WINDOW_BUTTON:
{
Context.listbox->addItem(L"Creata la Window");
Context.counter += 30;
if (Context.counter > 200)
Context.counter = 0;
IGUIWindow* window = env->addWindow(
rect<s32>(100 + Context.counter, 100 + Context.counter,
300 + Context.counter, 200 + Context.counter),
false, // modal?
L"Window");
env->addStaticText(L"Per favore
chiudimi",rect<s32>(35,35,150,50),
true, // border?
false, // wordwrap?
window);
}
return true;
case GUI_ID_FILE_OPEN_BUTTON:
Context.listbox->addItem(L"File apri");
env->addFileOpenDialog(L"Apri");
return true;
default:
return false;
}
break;
default:
break;
}
}
return false;
}
private:
SAppContext & Context;
};
IrrLicht
um 70 di 116
int main(void)
{
// setup iniziali
IrrlichtDevice *device =
createDevice(EDT_DIRECT3D9, dimension2d<u32>(640, 480), 16, false);
if (device == 0) return 1; // impossibile creare il driver
device->setWindowCaption(L"Interfaccia utente");
device->setResizable(true);
video::IVideoDriver* driver = device->getVideoDriver();
IGUIEnvironment* env = device->getGUIEnvironment();
// carico il font
IGUISkin* skin = env->getSkin();
IGUIFont* font = env->getFont("Media/fonthaettenschweiler.bmp");
if (font)skin->setFont(font);
skin->setFont(env->getBuiltInFont(), EGDF_TOOLTIP);
// aggiungo i tre bottoni
env->addButton(rect<s32>(10,240,110,240 + 32), 0, GUI_ID_QUIT_BUTTON,
L"Uscita", L"Esci dall'applicazione");
env->addButton(rect<s32>(10,280,110,280 + 32), 0,
GUI_ID_NEW_WINDOW_BUTTON,
L"Nuova finestra", L"Apri una nuova finestra");
env->addButton(rect<s32>(10,320,110,320 + 32), 0,
GUI_ID_FILE_OPEN_BUTTON,
L"File Apri", L"Apre un file");
// aggiungo la scrollbar
env->addStaticText(L"Controllo trasparenza", rect<s32>(150,20,350,40), true);
IGUIScrollBar* scrollbar = env->addScrollBar(true,
rect<s32>(150, 45, 350, 60), 0,
GUI_ID_TRANSPARENCY_SCROLL_BAR);
scrollbar->setMax(255);
// setto la posizione della scrollbar
scrollbar->setPos(env->getSkin()->getColor(EGDC_WINDOW).getAlpha());
env->addStaticText(L"ListBox:", rect<s32>(50,110,250,130), true);
IGUIListBox * listbox = env->addListBox(rect<s32>(50, 140, 250, 210));
env->addEditBox(L"Testo editabile", rect<s32>(350, 80, 550, 100));
// memorizzo i dati in context
SAppContext context;
context.device = device;
context.counter = 0;
context.listbox = listbox;
MyEventReceiver receiver(context);
device->setEventReceiver(&receiver);
// logo nell'angolo in alto a sinistra
env->addImage(driver->getTexture("Media/42.jpg"),position2d<int>(10,10));
while(device->run() && driver)
if (device->isWindowActive())
{
driver->beginScene(true, true, SColor(0,200,200,200));
env->drawAll();
driver->endScene();
}
device->drop();
return (0);
}
IrrLicht
um 71 di 116
IrrLicht
um 72 di 116
MOVIMENTI
INTRODUZIONE
Nel progetto Visual Studio ci sono i seguenti file.
All’interno della cartella del progetto creare le sotto cartelle LIBRERIE e MEDIA.
File MAIN.CPP
#include <I:\irrlicht-1.6.1\irrlicht-1.6.1\include\irrlicht.h>
#include <iostream>
using namespace irr;
using namespace video;
using namespace scene;
using namespace core;
using namespace video;
using namespace io;
using namespace gui;
class MyEventReceiver : public IEventReceiver
{public: virtual bool OnEvent(const SEvent& event)
{
if (event.EventType == irr::EET_KEY_INPUT_EVENT)
KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
return false;
}
// controlla quando il tasto è premuto
virtual bool IsKeyDown(EKEY_CODE keyCode)const
{ return KeyIsDown[keyCode];}
MyEventReceiver()
{ for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
KeyIsDown[i] = false;}
private: bool KeyIsDown[KEY_KEY_CODES_COUNT];
};
int main(void)
{
// creo il device
MyEventReceiver receiver;
// setup iniziali
IrrlichtDevice *device =
createDevice(EDT_DIRECT3D9, dimension2d<u32>(640, 480), 16, false);
if (device == 0) return 1; // impossibile creare il driver
IrrLicht
um 73 di 116
// creazione della videocamera
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
scene::ISceneNode * node = smgr->addSphereSceneNode();
if (node)
{
node->setPosition(core::vector3df(0,0,30));
node->setMaterialTexture(0, driver->getTexture("Media/wall.bmp"));
node->setMaterialFlag(video::EMF_LIGHTING, false);
}
scene::ISceneNode* n = smgr->addCubeSceneNode();
if (n)
{
n->setMaterialTexture(0, driver->getTexture("Media/t351sml.jpg"));
n->setMaterialFlag(video::EMF_LIGHTING, false);
scene::ISceneNodeAnimator* anim =
smgr->createFlyCircleAnimator(core::vector3df(0,0,30), 20.0f);
if (anim)
{
n->addAnimator(anim);
anim->drop();
}
}
scene::IAnimatedMeshSceneNode* anms =
smgr->addAnimatedMeshSceneNode(smgr->getMesh("Media/ninja.b3d"));
if (anms)
{
scene::ISceneNodeAnimator* anim =
smgr->createFlyStraightAnimator(core::vector3df(100,0,60),
core::vector3df(-100,0,60), 3500, true);
if (anim)
{
anms->addAnimator(anim);
anim->drop();
}
anms->setMaterialFlag(video::EMF_LIGHTING, false);
anms->setFrameLoop(0, 13);
anms->setAnimationSpeed(15);
anms->setMD2Animation(scene::EMAT_RUN);
anms->setScale(core::vector3df(2.f,2.f,2.f));
anms->setRotation(core::vector3df(0,-90,0));
anms->setMaterialTexture(0, driver->getTexture("Media/sydney.bmp"));
}
smgr->addCameraSceneNodeFPS();
device->getCursorControl()->setVisible(false);
// inserisco il logo
device->getGUIEnvironment()->addImage(driver->getTexture("Media/42.jpg"),
core::position2d<s32>(10,20));
gui::IGUIStaticText* diagnostics = device->getGUIEnvironment()->
addStaticText(L"LOGO", core::rect<s32>(10, 10, 400, 20));
diagnostics->setOverrideColor(video::SColor(255, 255, 255, 0));
int lastFPS = -1;
u32 then = device->getTimer()->getTime();
// questo è la velocità del movimento in unità per secondo
const f32 MOVEMENT_SPEED = 5.f;
while(device->run())
{
const u32 now = device->getTimer()->getTime();
// tempo in secondi
const f32 frameDeltaTime = (f32)(now - then) / 1000.f;
IrrLicht
um 74 di 116
then = now;
// controlla se il tasti W, S, A or D sono premuti, e muove la sfera
core::vector3df nodePosition = node->getPosition();
if(receiver.IsKeyDown(irr::KEY_KEY_W))
nodePosition.Y += MOVEMENT_SPEED * frameDeltaTime;
if(receiver.IsKeyDown(irr::KEY_KEY_S))
nodePosition.Y -= MOVEMENT_SPEED * frameDeltaTime;
if(receiver.IsKeyDown(irr::KEY_KEY_A))
nodePosition.X -= MOVEMENT_SPEED * frameDeltaTime;
if(receiver.IsKeyDown(irr::KEY_KEY_D))
nodePosition.X += MOVEMENT_SPEED * frameDeltaTime;
node->setPosition(nodePosition);
driver->beginScene(true, true, video::SColor(255,113,113,133));
smgr->drawAll();
// disegna le tre scene
device->getGUIEnvironment()->drawAll();
// disegna la GUI cioè il logo
driver->endScene();
int fps = driver->getFPS();
if (lastFPS != fps)
{ core::stringw tmp(L"Movimenti. Esempio [");
tmp += driver->getName(); tmp += L"] fps: "; tmp += fps;
device->setWindowCaption(tmp.c_str()); lastFPS = fps;
}
}
device->drop();return (0);
}
IrrLicht
um 75 di 116
EFFETTI SPECIALI
INTRODUZIONE
Nel progetto Visual Studio ci sono i seguenti file.
All’interno della cartella del progetto creare le sotto cartelle LIBRERIE e MEDIA.
File MAIN.CPP
/* effetti speciali:stencil buffer shadows, sistema particelle,
billboards, luce dinamica e acqua in superficie */
#include <I:\irrlicht-1.6.1\irrlicht-1.6.1\include\irrlicht.h>
#include <iostream>
using namespace irr;
using namespace video;
using namespace scene;
using namespace core;
using namespace video;
using namespace io;
using namespace gui;
int main(void)
{
char i;
printf("\n\nPer favore premi 'y' se desideri usare realtime shadows: ");
std::cin >> i;
const bool shadows = (i == 'y');
// setup iniziali
IrrlichtDevice *device =
createDevice(EDT_DIRECT3D9, core::dimension2d<u32>(640, 480), 16, false,shadows);
if (device == 0) return 1; // impossibile creare il driver
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
IrrLicht
um 76 di 116
// l'ambiente è una piccola stanza
scene::IAnimatedMesh* mesh = smgr->getMesh("Media/room.3ds");
smgr->getMeshManipulator()->makePlanarTextureMapping(mesh->getMesh(0), 0.004f);
scene::ISceneNode* node = 0;
node = smgr->addAnimatedMeshSceneNode(mesh);
node->setMaterialTexture(0, driver->getTexture("Media/wall.jpg"));
node->getMaterial(0).SpecularColor.set(0,0,0,0);
// primo effetto speciale: animazione dell'acqua
mesh = smgr->addHillPlaneMesh( "myHill",
core::dimension2d<f32>(20,20),
core::dimension2d<u32>(40,40), 0, 0,
core::dimension2d<f32>(0,0),
core::dimension2d<f32>(10,10));
node = smgr->addWaterSurfaceSceneNode(mesh->getMesh(0), 3.0f, 300.0f,
30.0f);
node->setPosition(core::vector3df(0,7,0));
node->setMaterialTexture(0, driver->getTexture("Media/stones.jpg"));
node->setMaterialTexture(1, driver->getTexture("Media/water.jpg"));
node->setMaterialType(video::EMT_REFLECTION_2_LAYER);
/* secondo effetto speciale:
la billboard trasparente è combinata con la luce dinamica */
// creo la luce
node = smgr->addLightSceneNode(0, core::vector3df(0,0,0),
video::SColorf(1.0f, 0.6f, 0.7f, 1.0f), 800.0f);
scene::ISceneNodeAnimator* anim = 0;
anim = smgr->createFlyCircleAnimator (core::vector3df(0,150,0),250.0f);
node->addAnimator(anim);
anim->drop();
// attacco il billboard alla luce
node = smgr->addBillboardSceneNode(node, core::dimension2d<f32>(50, 50));
node->setMaterialFlag(video::EMF_LIGHTING, false);
node->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
node->setMaterialTexture(0, driver->getTexture("Media/particlewhite.bmp"));
/* terzo effetto speciale: il sistema particella
// creo il sistema particella
scene::IParticleSystemSceneNode* ps =
smgr->addParticleSystemSceneNode(false);
scene::IParticleEmitter* em = ps->createBoxEmitter(
core::aabbox3d<f32>(-7,0,-7,7,1,7),
core::vector3df(0.0f,0.06f,0.0f),80,100,
video::SColor(0,255,255,255),
video::SColor(0,255,255,255),
800,2000,0,
core::dimension2df(10.f,10.f),
core::dimension2df(20.f,20.f));
ps->setEmitter(em);
em->drop();
scene::IParticleAffector* paf = ps->createFadeOutParticleAffector();
ps->addAffector(paf);
paf->drop();
ps->setPosition(core::vector3df(-70,60,40));
ps->setScale(core::vector3df(2,2,2));
ps->setMaterialFlag(video::EMF_LIGHTING, false);
ps->setMaterialFlag(video::EMF_ZWRITE_ENABLE, false);
IrrLicht
um 77 di 116
ps->setMaterialTexture(0, driver->getTexture("Media/fire.bmp"));
ps->setMaterialType(video::EMT_TRANSPARENT_VERTEX_ALPHA);
scene::IVolumeLightSceneNode * n = smgr->addVolumeLightSceneNode(0, -1,
32,32,
video::SColor(0, 255, 255, 255),
video::SColor(0, 0, 0, 0));
if (n)
{
n->setScale(core::vector3df(56.0f, 56.0f, 56.0f));
n->setPosition(core::vector3df(-120,50,40));
// carico la textures per l'animazione
core::array<video::ITexture*> textures;
for (s32 g=7; g > 0; --g)
{
core::stringc tmp;
tmp = "Media/portal";
tmp += g;
tmp += ".bmp";
video::ITexture* t = driver->getTexture( tmp.c_str() );
textures.push_back(t);
}
scene::ISceneNodeAnimator* glow = smgr->createTextureAnimator(textures, 150);
n->addAnimator(glow);
glow->drop();
}
/* quarto effetto speciale
carico il modello DirectX dwarf.x e lo inserisco nella scena per creare l'ombra
chiamo addShadowVolumeSceneNode il colore dell'ombra è solo aggiustabile
globalmente per tutte le ombre basta chiamare ISceneManager::setShadowColor */
// aggiungo il modello
mesh = smgr->getMesh("Media/dwarf.x");
scene::IAnimatedMeshSceneNode* anode = 0;
anode = smgr->addAnimatedMeshSceneNode(mesh);
anode->setPosition(core::vector3df(-50,20,-60));
anode->setAnimationSpeed(15);
// aggiungo l'ombra
anode->addShadowVolumeSceneNode();
smgr->setShadowColor(video::SColor(150,0,0,0));
anode->setScale(core::vector3df(2,2,2));
anode->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, true);
scene::ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS();
camera->setPosition(core::vector3df(-50,50,-150));
device->getCursorControl()->setVisible(false);
s32 lastFPS = -1;
while(device->run())
if (device->isWindowActive())
{
driver->beginScene(true, true, 0);
smgr->drawAll();
driver->endScene();
const s32 fps = driver->getFPS();
if (lastFPS != fps)
{
core::stringw str = L"Effetti speciali. Esempio [";
str += driver->getName();
str += "] FPS:";
str += fps;
device->setWindowCaption(str.c_str());
IrrLicht
um 78 di 116
lastFPS = fps;
}
}
device->drop();
return (0);
}
IrrLicht
um 79 di 116
SPLITSCREEN
INTRODUZIONE
Creo una viewport e la divido in 4 parti, con 3 videocamere fisse e una, invece, controllata
dall’utente, il tasto S abilita/disabilita lo splitScreen.
Nel progetto Visual Studio ci sono i seguenti file.
All’interno della cartella del progetto creare le sotto cartelle LIBRERIE e MEDIA.
File MAIN.CPP
#include <I:\irrlicht-1.6.1\irrlicht-1.6.1\include\irrlicht.h>
#include <iostream>
using namespace irr;
using namespace video;
using namespace scene;
using namespace core;
using namespace video;
using namespace io;
using namespace gui;
const int ResX=640;
// risoluzione
const int ResY=480;
const bool fullScreen=false;
bool SplitScreen=true;
// splitScreen
ICameraSceneNode *camera[4]={0,0,0,0};
// videocamera
class MyEventReceiver : public IEventReceiver
{public:
virtual bool OnEvent(const SEvent& event)
{// tasto S abilita/disabilita lo splitScreen
if (event.EventType == irr::EET_KEY_INPUT_EVENT &&
event.KeyInput.Key == KEY_KEY_S && event.KeyInput.PressedDown)
{
SplitScreen = !SplitScreen;
return true;
}
if (camera[3]) return camera[3]->OnEvent(event);
return false;
}
};
int main(void)
{
// istanza di EventReceiver
MyEventReceiver receiver;
IrrLicht
um 80 di 116
// setup iniziali
IrrlichtDevice *device =
createDevice(EDT_DIRECT3D9, dimension2d<u32>(ResX,ResY), 32, fullScreen,
false, false, &receiver);
if (device == 0) return 1; // impossibile creare il driver
ISceneManager *smgr = device->getSceneManager();
IVideoDriver *driver = device->getVideoDriver();
// carico il modello
IAnimatedMesh *model = smgr->getMesh("Media/sydney.md2");
if (!model) return 1;
IAnimatedMeshSceneNode *model_node=smgr->addAnimatedMeshSceneNode(model);
// carico la texture
if (model_node)
{
ITexture *texture = driver->getTexture("Media/sydney.bmp");
model_node->setMaterialTexture(0,texture);
model_node->setMD2Animation(scene::EMAT_RUN);
// disabilito lighting
model_node->setMaterialFlag(EMF_LIGHTING,false);
}
// carico la mappa
device->getFileSystem()->addZipFileArchive("Media/map-20kdm2.pk3");
IAnimatedMesh *map = smgr->getMesh("20kdm2.bsp");
if (map)
{ISceneNode *map_node = smgr->addOctTreeSceneNode(map->getMesh(0));
// setto la posizione
map_node->setPosition(vector3df(-850,-220,-850));
}
// creo 4 videocamere 3 fisse ed una controllata dall'utente
// front
camera[0] = smgr->addCameraSceneNode(0, vector3df(50,0,0), vector3df(0,0,0));
// top
camera[1] = smgr->addCameraSceneNode(0, vector3df(0,50,0), vector3df(0,0,0));
// left
camera[2] = smgr->addCameraSceneNode(0, vector3df(0,0,50), vector3df(0,0,0));
// controllata dall'utente
camera[3] = smgr->addCameraSceneNodeFPS();
// inizio posizione di Sydney
if (camera[3]) camera[3]->setPosition(core::vector3df(-50,0,-50));
// nascondo il mouse
device->getCursorControl()->setVisible(false);
// conto gli FPS
int lastFPS = -1;
while(device->run())
{
// setto la viewpoint
driver->setViewPort(rect<s32>(0,0,ResX,ResY));
driver->beginScene(true,true,SColor(255,100,100,100));
// se uso lo splitScreen
if (SplitScreen)
{
// attivo la videocamera1
smgr->setActiveCamera(camera[0]);
// setto la viewpoint left top)
driver->setViewPort(rect<s32>(0,0,ResX/2,ResY/2));
// disegno la scena
smgr->drawAll();
IrrLicht
um 81 di 116
// attivo la videocamera2
smgr->setActiveCamera(camera[1]);
// setto la viewpoint (right top)
driver->setViewPort(rect<s32>(ResX/2,0,ResX,ResY/2));
// disegno la scena
smgr->drawAll();
// attivo la videocamera3
smgr->setActiveCamera(camera[2]);
// setto la viewpoint (left bottom)
driver->setViewPort(rect<s32>(0,ResY/2,ResX/2,ResY));
// disegno la scena
smgr->drawAll();
// setto la viewpoint (right bottom)
driver->setViewPort(rect<s32>(ResX/2,ResY/2,ResX,ResY));
}
smgr->setActiveCamera(camera[3]);
// attivo la videocamera4
smgr->drawAll();driver->endScene(); // disegno la scena
if (driver->getFPS() != lastFPS)
{ lastFPS = driver->getFPS();
core::stringw tmp = L"SplitScreen. Esempio (FPS: ";
tmp += lastFPS; tmp += ")"; device->setWindowCaption(tmp.c_str());
}
}
device->drop();return (0);
}
IrrLicht
um 82 di 116
MOUSE & JOYSTICK
INTRODUZIONE
Nel progetto Visual Studio ci sono i seguenti file.
All’interno della cartella del progetto creare la sotto cartella LIBRERIE.
File MAIN.CPP
#include <I:\irrlicht-1.6.1\irrlicht-1.6.1\include\irrlicht.h>
#include <iostream>
using namespace irr;
using namespace video;
using namespace scene;
using namespace core;
using namespace video;
using namespace io;
using namespace gui;
class MyEventReceiver : public IEventReceiver
{ public:
// record che memorizza lo stato del mouse
struct SMouseState
{
core::position2di Position;
bool LeftButtonDown;
SMouseState() : LeftButtonDown(false) { }
} MouseState;
virtual bool OnEvent(const SEvent& event)
{
// ricorda lo stato del mouse
if (event.EventType == irr::EET_MOUSE_INPUT_EVENT)
{
switch(event.MouseInput.Event) {
case EMIE_LMOUSE_PRESSED_DOWN:
MouseState.LeftButtonDown = true;
break;
case EMIE_LMOUSE_LEFT_UP:
MouseState.LeftButtonDown = false;
break;
case EMIE_MOUSE_MOVED:
MouseState.Position.X = event.MouseInput.X;
MouseState.Position.Y = event.MouseInput.Y;
break;
default:
break;
}
}
IrrLicht
um 83 di 116
if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT
&& event.JoystickEvent.Joystick == 0)
{JoystickState = event.JoystickEvent;}
return false;
}
const SEvent::SJoystickEvent & GetJoystickState(void) const
{return JoystickState;}
const SMouseState & GetMouseState(void) const
{ return MouseState;}
MyEventReceiver() { }
private:
SEvent::SJoystickEvent JoystickState;
};
int main(void)
{
// istanza di EventReceiver
MyEventReceiver receiver;
// setup iniziali
IrrlichtDevice *device =
createDevice(EDT_DIRECT3D9, core::dimension2d<u32>(640, 480), 16,
false, false, false, &receiver);
if (device == 0) return 1; // impossibile creare il driver
core::array<SJoystickInfo> joystickInfo;
if(device->activateJoysticks(joystickInfo))
{
std::cout << "Il supporto Joystick e' abilitato e ci sono " << joystickInfo.size()
<< " joystick presenti." << std::endl;
for(u32 joystick = 0; joystick < joystickInfo.size(); ++joystick)
{
std::cout << "Joystick " << joystick << ":" << std::endl;
std::cout<<"\tNome: ‘"<<joystickInfo[joystick].Name.c_str() << "‘" << std::endl;
std::cout << "\tAssi: " << joystickInfo[joystick].Axes << std::endl;
std::cout << "\tBottoni: " << joystickInfo[joystick].Buttons << std::endl;
std::cout << "\tè: ";
switch(joystickInfo[joystick].PovHat)
{
case SJoystickInfo::POV_HAT_PRESENT:
std::cout << "presente" << std::endl;
break;
case SJoystickInfo::POV_HAT_ABSENT:
std::cout << "assente" << std::endl;
break;
case SJoystickInfo::POV_HAT_UNKNOWN:
default:
std::cout << "sconosciuto" << std::endl;
break;
}
}
}
else
{std::cout << "Il supporto del Joystick non e' abilitato." << std::endl;}
core::stringw tmp = L"Joystick. Esempio. (";
tmp += joystickInfo.size();
tmp += " joysticks)";
device->setWindowCaption(tmp.c_str());
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
IrrLicht
um 84 di 116
scene::ISceneNode * node = smgr->addMeshSceneNode(
smgr->addArrowMesh( "Arrow",
video::SColor(255, 255, 0, 0),
video::SColor(255, 0, 255, 0),
16,16,
2.f, 1.3f,
0.1f, 0.6f
)
);
node->setMaterialFlag(video::EMF_LIGHTING, false);
scene::ICameraSceneNode * camera = smgr->addCameraSceneNode();
camera->setPosition(core::vector3df(0, 0, -10));
u32 then = device->getTimer()->getTime();
const f32 MOVEMENT_SPEED = 5.f;
while(device->run())
{
const u32 now = device->getTimer()->getTime();
const f32 frameDeltaTime = (f32)(now - then) / 1000.f;
then = now;
bool movedWithJoystick = false;
core::vector3df nodePosition = node->getPosition();
if(joystickInfo.size() > 0)
{
f32 moveHorizontal = 0.f;
f32 moveVertical = 0.f;
const SEvent::SJoystickEvent & joystickData =
receiver.GetJoystickState();
const f32 DEAD_ZONE = 0.05f;
moveHorizontal =
(f32)joystickData.Axis[SEvent::SJoystickEvent::AXIS_X] /
32767.f;
if(fabs(moveHorizontal) < DEAD_ZONE)
moveHorizontal = 0.f;
moveVertical =
(f32)joystickData.Axis[SEvent::SJoystickEvent::AXIS_Y] / 32767.f;
if(fabs(moveVertical) < DEAD_ZONE)
moveVertical = 0.f;
const u16 povDegrees = joystickData.POV / 100;
if(povDegrees < 360)
{
if(povDegrees > 0 && povDegrees < 180)moveHorizontal = 1.f;
else if(povDegrees > 180)moveHorizontal = -1.f;
if(povDegrees > 90 && povDegrees < 270)moveVertical = -1.f;
else if(povDegrees > 270 || povDegrees < 90)moveVertical =
+1.f;
}
if(!core::equals(moveHorizontal, 0.f) || !core::equals(moveVertical, 0.f))
{
nodePosition.X += MOVEMENT_SPEED * frameDeltaTime *
moveHorizontal;
nodePosition.Y += MOVEMENT_SPEED * frameDeltaTime *
moveVertical;
movedWithJoystick = true;
}
}
if(!movedWithJoystick)
IrrLicht
um 85 di 116
{
core::line3df ray = smgr->getSceneCollisionManager()>getRayFromScreenCoordinates(
receiver.GetMouseState().Position, camera);
core::plane3df plane(nodePosition, core::vector3df(0, 0, -1));
core::vector3df mousePosition;
if(plane.getIntersectionWithLine(ray.start, ray.getVector(), mousePosition))
{ core::vector3df toMousePosition(mousePosition - nodePosition);
const f32 availableMovement = MOVEMENT_SPEED * frameDeltaTime;
if(toMousePosition.getLength() <= availableMovement)
nodePosition = mousePosition;
else
nodePosition += toMousePosition.normalize() * availableMovement;
}
}
node->setPosition(nodePosition);
node->setMaterialFlag(video::EMF_LIGHTING,
receiver.GetMouseState().LeftButtonDown);
driver->beginScene(true, true, video::SColor(255,113,113,133));
smgr->drawAll();
driver->endScene();
}
device->drop();
return (0);
}
IrrLicht
um 86 di 116
MENU
INTRODUZIONE
Nel progetto Visual Studio ci sono i seguenti file.
All’interno della cartella del progetto creare le sotto cartelle LIBRERIE e MEDIA.
File MAIN.CPP
/* costruisco una semplice mesh usando le interfacce API, creo ed uso bottoni, finestre,
toolbar, menu, combobox, tabcontrol, editbox, immagini, messagebox */
#include <I:\irrlicht-1.6.1\irrlicht-1.6.1\include\irrlicht.h>
#include <iostream>
using namespace irr;
using namespace video;
using namespace scene;
using namespace core;
using namespace video;
using namespace io;
using namespace gui;
IrrlichtDevice *Device = 0;
core::stringc StartUpModelFile;
core::stringw MessageText;
core::stringw Caption;
scene::ISceneNode* Model = 0;
scene::ISceneNode* SkyBox = 0;
bool Octree=false;
bool useLight=false;
scene::ICameraSceneNode* Camera[2] = {0, 0};
// valori usati per identificare gli elementi della GUI
IrrLicht
um 87 di 116
enum
{
GUI_ID_DIALOG_ROOT_WINDOW = 0x10000,
GUI_ID_X_SCALE,
GUI_ID_Y_SCALE,
GUI_ID_Z_SCALE,
GUI_ID_OPEN_MODEL,
GUI_ID_SET_MODEL_ARCHIVE,
GUI_ID_LOAD_AS_OCTREE,
GUI_ID_SKY_BOX_VISIBLE,
GUI_ID_TOGGLE_DEBUG_INFO,
GUI_ID_DEBUG_OFF,
GUI_ID_DEBUG_BOUNDING_BOX,
GUI_ID_DEBUG_NORMALS,
GUI_ID_DEBUG_SKELETON,
GUI_ID_DEBUG_WIRE_OVERLAY,
GUI_ID_DEBUG_HALF_TRANSPARENT,
GUI_ID_DEBUG_BUFFERS_BOUNDING_BOXES,
GUI_ID_DEBUG_ALL,
GUI_ID_MODEL_MATERIAL_SOLID,
GUI_ID_MODEL_MATERIAL_TRANSPARENT,
GUI_ID_MODEL_MATERIAL_REFLECTION,
GUI_ID_CAMERA_MAYA,
GUI_ID_CAMERA_FIRST_PERSON,
GUI_ID_POSITION_TEXT,
GUI_ID_ABOUT,
GUI_ID_QUIT,
MAX_FRAMERATE = 1000,
DEFAULT_FRAMERATE = 30
};
void setActiveCamera(scene::ICameraSceneNode* newActive)
{
if (0 == Device)
return;
scene::ICameraSceneNode * active = Device->getSceneManager()->
getActiveCamera();
active->setInputReceiverEnabled(false);
newActive->setInputReceiverEnabled(true);
Device->getSceneManager()->setActiveCamera(newActive);
}
// la funzione showAboutText visualizza una messagebox
void showAboutText()
{
// crea una messagebox con il testo caricato da un file xml
Device->getGUIEnvironment()->addMessageBox(Caption.c_str(),
MessageText.c_str());
}
// la funzione loadModel carica e visualizza un modello usando
addAnimatedMeshSceneNode
void loadModel(const c8* fn)
{
core::stringc filename(fn);
core::stringc extension;
core::getFileNameExtension(extension, filename);
extension.make_lower();
if (extension == ".jpg" || extension == ".pcx" ||
extension == ".png" || extension == ".ppm" ||
IrrLicht
um 88 di 116
{
extension == ".pgm" || extension == ".pbm" ||
extension == ".psd" || extension == ".tga" ||
extension == ".bmp" || extension == ".wal")
video::ITexture * texture = Device->getVideoDriver()->getTexture( filename );
if ( texture && Model )
{
Device->getVideoDriver()->removeTexture(texture);
texture = Device->getVideoDriver()->getTexture( filename );
Model->setMaterialTexture(0, texture);
}
return;
}
else if (extension == ".pk3" || extension == ".zip")
{
Device->getFileSystem()->addZipFileArchive(filename.c_str());
return;
}
else if (extension == ".pak")
{
Device->getFileSystem()->addPakFileArchive(filename.c_str());
return;
}
// carica il modello nel motore
if (Model)Model->remove();
Model = 0;
scene::IAnimatedMesh* m = Device->getSceneManager()->
getMesh( filename.c_str() );
if (!m)
{
// il modello non può essere caricato
if (StartUpModelFile != filename)
Device->getGUIEnvironment()->addMessageBox(
Caption.c_str(), L"Il modello non può essere caricato. " \
L"Non è supportato il formato del file.");
return;
}
if (Octree) Model = Device->getSceneManager()->
addOctTreeSceneNode(m->getMesh(0));
else
{
scene::IAnimatedMeshSceneNode* animModel = Device->
getSceneManager()->addAnimatedMeshSceneNode(m);
animModel->setAnimationSpeed(30);
Model = animModel;
}
Model->setMaterialFlag(video::EMF_LIGHTING, useLight);
Model->setMaterialFlag(video::EMF_NORMALIZE_NORMALS, useLight);
// Model->setMaterialFlag(video::EMF_BACK_FACE_CULLING, false);
Model->setDebugDataVisible(scene::EDS_OFF);
gui::IGUIContextMenu* menu = (gui::IGUIContextMenu*)Device->
getGUIEnvironment()->getRootGUIElement()->
getElementFromId(GUI_ID_TOGGLE_DEBUG_INFO, true);
if (menu)
for(int item = 1; item < 6; ++item)menu->setItemChecked(item, false);
IGUIElement* toolboxWnd = Device->getGUIEnvironment()->
getRootGUIElement()->getElementFromId(GUI_ID_DIALOG_ROOT_WINDOW, true);
if ( toolboxWnd )
{
toolboxWnd->getElementFromId(GUI_ID_X_SCALE, true)->setText(L"1.0");
toolboxWnd->getElementFromId(GUI_ID_Y_SCALE, true)->setText(L"1.0");
IrrLicht
um 89 di 116
toolboxWnd->getElementFromId(GUI_ID_Z_SCALE, true)->setText(L"1.0");
}
}
// la funzione createToolBox crea una toolbox che contiene un tabcontrol
void createToolBox()
{
// rimuove la toolbox se esiste
IGUIEnvironment* env = Device->getGUIEnvironment();
IGUIElement* root = env->getRootGUIElement();
IGUIElement* e = root->getElementFromId(GUI_ID_DIALOG_ROOT_WINDOW, true);
if (e) e->remove();
// crea la toolbox
IGUIWindow* wnd = env->addWindow(core::rect<s32>(600,45,800,480),
false, L"Toolset", 0, GUI_ID_DIALOG_ROOT_WINDOW);
// crea il tabcontrol
IGUITabControl* tab = env->addTabControl(
core::rect<s32>(2,20,800-602,480-7), wnd, true, true);
IGUITab* t1 = tab->addTab(L"Config");
env->addStaticText(L"Scale:",core::rect<s32>(10,20,150,45), false, false, t1);
env->addStaticText(L"X:", core::rect<s32>(22,48,40,66), false, false, t1);
env->addEditBox(L"1.0", core::rect<s32>(40,46,130,66), true, t1, GUI_ID_X_SCALE);
env->addStaticText(L"Y:", core::rect<s32>(22,82,40,GUI_ID_OPEN_MODEL), false, false,
t1);
env->addEditBox(L"1.0", core::rect<s32>(40,76,130,96), true, t1, GUI_ID_Y_SCALE);
env->addStaticText(L"Z:", core::rect<s32>(22,108,40,126), false, false, t1);
env->addEditBox(L"1.0", core::rect<s32>(40,106,130,126), true, t1, GUI_ID_Z_SCALE);
env->addButton(core::rect<s32>(10,134,85,165), t1, 1101, L"Set");
// aggiuge il controllo di trasparenza
env->addStaticText(L"Controllo trasparenza",core::rect<s32>(10,200,150,225),true, false,
t1);
IGUIScrollBar* scrollbar = env->
addScrollBar(true,core::rect<s32>(10,225,150,240),t1,104);
scrollbar->setMax(255);
scrollbar->setPos(255);
// aggiunge il controllo di framerate
env->addStaticText(L"Framerate",core::rect<s32>(10,240,150,265), true, false, t1);
scrollbar = env->addScrollBar(true,core::rect<s32>(10,265,150,280), t1, 105);
scrollbar->setMax(MAX_FRAMERATE);
scrollbar->setPos(DEFAULT_FRAMERATE);
// logo
root->bringToFront(root->getElementFromId(666, true));
}
// legge tutti gli eventi inviati dalla GUI
class MyEventReceiver : public IEventReceiver
{ public:
virtual bool OnEvent(const SEvent& event)
{
// tasto ESC
if (event.EventType == EET_KEY_INPUT_EVENT &&
event.KeyInput.PressedDown == false)
{
if (event.KeyInput.Key == irr::KEY_ESCAPE)
{
if (Device)
{
scene::ICameraSceneNode * camera =
Device->getSceneManager()->getActiveCamera();
if (camera)
{camera->setInputReceiverEnabled( !camera->
IrrLicht
um 90 di 116
isInputReceiverEnabled() );}
return true;
}
}
else if (event.KeyInput.Key == irr::KEY_F1)
{
if (Device)
{
IGUIElement* elem = Device->getGUIEnvironment()->
getRootGUIElement()->getElementFromId(GUI_ID_POSITION_TEXT);
if (elem) elem->setVisible(!elem->isVisible());
}
}
else if (event.KeyInput.Key == irr::KEY_KEY_M)
{
if (Device) Device->minimizeWindow();}
else if (event.KeyInput.Key == irr::KEY_KEY_L)
{
useLight=!useLight;
if (Model)
{Model->setMaterialFlag(video::EMF_LIGHTING, useLight);
Model->
setMaterialFlag(video::EMF_NORMALIZE_NORMALS, useLight);
}
}
}
if (event.EventType == EET_GUI_EVENT)
{
s32 id = event.GUIEvent.Caller->getID();
IGUIEnvironment* env = Device->getGUIEnvironment();
switch(event.GUIEvent.EventType)
{ case EGET_MENU_ITEM_SELECTED:
{
// ho fatto clic sul menu
IGUIContextMenu* menu =
(IGUIContextMenu*)event.GUIEvent.Caller;
s32 id = menu->getItemCommandId(menu->
getSelectedItem());
switch(id)
{
case GUI_ID_OPEN_MODEL: // File -> Apri
env->addFileOpenDialog(L"Seleziona il modello
del file da aprire");
break;
case GUI_ID_SET_MODEL_ARCHIVE: // File -> Archivio
env->addFileOpenDialog(L"Seleziona il gioco
archivio/directory");
break;
case GUI_ID_LOAD_AS_OCTREE:
// File -> LoadAsOctree
Octree = !Octree;
menu->setItemChecked(menu->
getSelectedItem(), Octree);
break;
case GUI_ID_QUIT: // File -> Esci
Device->closeDevice();
break;
case GUI_ID_SKY_BOX_VISIBLE: // View -> Skybox
menu->setItemChecked(menu->
getSelectedItem(), !menu->isItemChecked(menu->getSelectedItem()));
IrrLicht
um 91 di 116
SkyBox->setVisible(!SkyBox->isVisible());
break;
case GUI_ID_DEBUG_OFF: // View -> Debug Information
menu->setItemChecked(menu->getSelectedItem()+1, false);
menu->setItemChecked(menu->getSelectedItem()+2, false);
menu->setItemChecked(menu->getSelectedItem()+3, false);
menu->setItemChecked(menu->getSelectedItem()+4, false);
menu->setItemChecked(menu->getSelectedItem()+5, false);
menu->setItemChecked(menu->getSelectedItem()+6, false);
if (Model)
Model->setDebugDataVisible(scene::EDS_OFF);
break;
case GUI_ID_DEBUG_BOUNDING_BOX:
// View -> Debug Information
menu->setItemChecked(menu->
getSelectedItem(), !menu->isItemChecked(menu->getSelectedItem()));
if (Model)
Model->setDebugDataVisible((scene::E_DEBUG_SCENE_TYPE)(Model->
isDebugDataVisible()^scene::EDS_BBOX));
break;
case GUI_ID_DEBUG_NORMALS:
// View -> Debug Information
menu->setItemChecked(menu->
getSelectedItem(), !menu->isItemChecked(menu->getSelectedItem()));
if (Model)
Model->
setDebugDataVisible((scene::E_DEBUG_SCENE_TYPE)(Model->
isDebugDataVisible()^scene::EDS_NORMALS));
break;
case GUI_ID_DEBUG_SKELETON:
// View -> Debug Information
menu->setItemChecked(menu->
getSelectedItem(), !menu->isItemChecked(menu->getSelectedItem()));
if (Model)
Model->
setDebugDataVisible((scene::E_DEBUG_SCENE_TYPE)(Model->
isDebugDataVisible()^scene::EDS_SKELETON));
break;
case GUI_ID_DEBUG_WIRE_OVERLAY:
// View -> Debug Information
menu->setItemChecked(menu->
getSelectedItem(), !menu->isItemChecked(menu->getSelectedItem()));
if (Model)
Model->
setDebugDataVisible((scene::E_DEBUG_SCENE_TYPE)(Model->
isDebugDataVisible()^scene::EDS_MESH_WIRE_OVERLAY));
break;
case GUI_ID_DEBUG_HALF_TRANSPARENT:
// View -> Debug Information
menu->setItemChecked(menu->
getSelectedItem(), !menu->isItemChecked(menu->getSelectedItem()));
if (Model)
Model->
setDebugDataVisible((scene::E_DEBUG_SCENE_TYPE)(Model->
IrrLicht
um 92 di 116
isDebugDataVisible()^scene::EDS_HALF_TRANSPARENCY));
break;
case GUI_ID_DEBUG_BUFFERS_BOUNDING_BOXES:
// View -> Debug Information
menu->setItemChecked(menu->
getSelectedItem(), !menu->isItemChecked(menu->getSelectedItem()));
if (Model)
Model->
setDebugDataVisible((scene::E_DEBUG_SCENE_TYPE)(Model->
isDebugDataVisible()^scene::EDS_BBOX_BUFFERS));
break;
case GUI_ID_DEBUG_ALL:
// View -> Debug Information
menu->setItemChecked(menu->getSelectedItem()-1, true);
menu->setItemChecked(menu->getSelectedItem()-2, true);
menu->setItemChecked(menu->getSelectedItem()-3, true);
menu->setItemChecked(menu->getSelectedItem()-4, true);
menu->setItemChecked(menu->getSelectedItem()-5, true);
menu->setItemChecked(menu->getSelectedItem()-6, true);
if (Model)
Model->setDebugDataVisible(scene::EDS_FULL);
break;
case GUI_ID_ABOUT: // ? -> About
showAboutText();
break;
case GUI_ID_MODEL_MATERIAL_SOLID:
// View -> Material -> Solid
if (Model)
Model->setMaterialType(video::EMT_SOLID);
break;
case GUI_ID_MODEL_MATERIAL_TRANSPARENT: //
View -> Material -> Transparent
if (Model)
Model->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
break;
case GUI_ID_MODEL_MATERIAL_REFLECTION:
// View -> Material -> Reflection
if (Model)
Model->setMaterialType(video::EMT_SPHERE_MAP);
break;
case GUI_ID_CAMERA_MAYA:
setActiveCamera(Camera[0]);
break;
case GUI_ID_CAMERA_FIRST_PERSON:
setActiveCamera(Camera[1]);
break;
}
break;
}
case EGET_FILE_SELECTED:
{
// carica il modello del file, selezionato nella dialogbox
IGUIFileOpenDialog* dialog =
(IGUIFileOpenDialog*)event.GUIEvent.Caller;
loadModel(core::stringc(dialog->getFileName()).c_str());
IrrLicht
um 93 di 116
}
break;
case EGET_SCROLL_BAR_CHANGED:
// controllo trasparenza skin
if (id == 104)
{
const s32 pos =
((IGUIScrollBar*)event.GUIEvent.Caller)->getPos();
for (s32 i=0; i<irr::gui::EGDC_COUNT ; ++i)
{
video::SColor col = env->getSkin()->
getColor((EGUI_DEFAULT_COLOR)i);
col.setAlpha(pos);
env->getSkin()->setColor((EGUI_DEFAULT_COLOR)i, col);
}
}
else if (id == 105)
{
const s32 pos =
((IGUIScrollBar*)event.GUIEvent.Caller)->getPos();
if (scene::ESNT_ANIMATED_MESH == Model->getType())
((scene::IAnimatedMeshSceneNode*)Model)->
setAnimationSpeed((f32)pos);
}
break;
case EGET_COMBO_BOX_CHANGED:
// controllo anti-aliasing/filtering
if (id == 108)
{
s32 pos = ((IGUIComboBox*)event.GUIEvent.Caller)->
getSelected();
switch (pos)
{
case 0:
if (Model)
{
Model->
setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
Model->setMaterialFlag(video::EMF_TRILINEAR_FILTER, false);
Model->setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, false);
}
break;
case 1:
if (Model)
{
Model->
setMaterialFlag(video::EMF_BILINEAR_FILTER, true);
Model->
setMaterialFlag(video::EMF_TRILINEAR_FILTER, false);
}
break;
case 2:
if (Model)
{
Model->
setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
Model->
setMaterialFlag(video::EMF_TRILINEAR_FILTER, true);
}
break;
case 3:
if (Model){Model->
IrrLicht
um 94 di 116
setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, true);}
break;
case 4:
if (Model){Model->
setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, false);}
break;
}
}
break;
case EGET_BUTTON_CLICKED:
switch(id)
{case 1101:
{
// setto la scala
gui::IGUIElement* root = env->getRootGUIElement();
core::vector3df scale;
core::stringc s;
s = root->getElementFromId(GUI_ID_X_SCALE, true)->getText();
scale.X = (f32)atof(s.c_str());
s = root->getElementFromId(GUI_ID_Y_SCALE, true)->getText();
scale.Y = (f32)atof(s.c_str());
s = root->getElementFromId(GUI_ID_Z_SCALE, true)->getText();
scale.Z = (f32)atof(s.c_str());
if (Model)Model->setScale(scale);
}
break;
case 1102:
env->addFileOpenDialog(L"Seleziona il modello del file da aprire");
break;
case 1103:
showAboutText();
break;
case 1104:
createToolBox();
break;
case 1105:
env->addFileOpenDialog(L"Seleziona il gioco archivio/directory");
break;
}
break;
default:
break;
}
}
return false;
}
};
int main(int argc, char* argv[])
{
MyEventReceiver receiver;
Device = createDevice(EDT_DIRECT3D9, core::dimension2d<u32>(800, 600),
16, false, false, false, &receiver);
if (Device == 0)
return 1; // could not create selected driver.
Device->setResizable(true);
Device->setWindowCaption(L"Carico...");
IrrLicht
um 95 di 116
video::IVideoDriver* driver = Device->getVideoDriver();
IGUIEnvironment* env = Device->getGUIEnvironment();
scene::ISceneManager* smgr = Device->getSceneManager();
smgr->getParameters()->
setAttribute(scene::COLLADA_CREATE_SCENE_INSTANCES, true);
driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, true);
smgr->addLightSceneNode();
smgr->addLightSceneNode(0, core::vector3df(200,200,200),
video::SColorf(1.0f,1.0f,1.0f),2000);
// aggiungo la cartella Media
Device->getFileSystem()->addFolderFileArchive("Media/");
// leggo il file xml di configurazione
io::IXMLReader* xml = Device->getFileSystem()->createXMLReader(
L"config.xml");
while(xml && xml->read())
{
switch(xml->getNodeType())
{case io::EXN_TEXT:
MessageText = xml->getNodeData();
break;
case io::EXN_ELEMENT:
{
if (core::stringw("startUpModel") == xml->getNodeName())
StartUpModelFile = xml->getAttributeValue(L"file");
else
if (core::stringw("messageText") == xml->getNodeName())
Caption = xml->getAttributeValue(L"caption");
}
break;
default:
break;
}
}
if (xml)xml->drop();
if (argc > 1) StartUpModelFile = argv[1];
// creo il menu e per ogni menu un sotto menu
// setto il font
IGUISkin* skin = env->getSkin();
IGUIFont* font = env->getFont("fonthaettenschweiler.bmp");
if (font)skin->setFont(font);
// creo il menu
gui::IGUIContextMenu* menu = env->addMenu();
menu->addItem(L"File", -1, true, true);
menu->addItem(L"View", -1, true, true);
menu->addItem(L"Camera", -1, true, true);
menu->addItem(L"?", -1, true, true);
gui::IGUIContextMenu* submenu;
submenu = menu->getSubMenu(0);
submenu->addItem(L"Apro un modello di file & texture...",GUI_ID_OPEN_MODEL);
submenu->addItem(L"Seleziona l'archivio...", GUI_ID_SET_MODEL_ARCHIVE);
submenu->addItem(L"Carica come Octree", GUI_ID_LOAD_AS_OCTREE);
submenu->addSeparator();
submenu->addItem(L"Esci", GUI_ID_QUIT);
submenu = menu->getSubMenu(1);
submenu->addItem(L"sky box visibile", GUI_ID_SKY_BOX_VISIBLE, true, false, true);
IrrLicht
um 96 di 116
submenu->addItem(L"Seleziona il modello di informazioni di debug",
GUI_ID_TOGGLE_DEBUG_INFO, true, true);
submenu->addItem(L"Seleziona il tipo di modello", -1, true, true );
submenu = submenu->getSubMenu(1);
submenu->addItem(L"Off", GUI_ID_DEBUG_OFF);
submenu->addItem(L"Bounding Box", GUI_ID_DEBUG_BOUNDING_BOX);
submenu->addItem(L"Normals", GUI_ID_DEBUG_NORMALS);
submenu->addItem(L"Skeleton", GUI_ID_DEBUG_SKELETON);
submenu->addItem(L"Wire overlay", GUI_ID_DEBUG_WIRE_OVERLAY);
submenu->addItem(L"Half-Transparent", GUI_ID_DEBUG_HALF_TRANSPARENT);
submenu->addItem(L"Buffers bounding boxes",
GUI_ID_DEBUG_BUFFERS_BOUNDING_BOXES);
submenu->addItem(L"All", GUI_ID_DEBUG_ALL);
submenu = menu->getSubMenu(1)->getSubMenu(2);
submenu->addItem(L"Solido", GUI_ID_MODEL_MATERIAL_SOLID);
submenu->addItem(L"Trasparente", GUI_ID_MODEL_MATERIAL_TRANSPARENT);
submenu->addItem(L"Riflessione", GUI_ID_MODEL_MATERIAL_REFLECTION);
submenu = menu->getSubMenu(2);
submenu->addItem(L"Stile Maya", GUI_ID_CAMERA_MAYA);
submenu->addItem(L"Prima Persona", GUI_ID_CAMERA_FIRST_PERSON);
submenu = menu->getSubMenu(3);
submenu->addItem(L"Informazioni su...", GUI_ID_ABOUT);
// creo la toolbar
gui::IGUIToolBar* bar = env->addToolBar();
video::ITexture* image = driver->getTexture("open.png");
bar->addButton(1102, 0, L"Apro il modello",image, 0, false, true);
image = driver->getTexture("tools.png");
bar->addButton(1104, 0, L"Apro gli strumenti",image, 0, false, true);
image = driver->getTexture("zip.png");
bar->addButton(1105, 0, L"Seleziona l'archivio",image, 0, false, true);
image = driver->getTexture("help.png");
bar->addButton(1103, 0, L"Apro ?", image, 0, false, true);
// creo la combobox
gui::IGUIComboBox* box = env->addComboBox(core::rect<s32>(250,4,350,23), bar, 108);
box->addItem(L"No filtering");
box->addItem(L"Bilinear");
box->addItem(L"Trilinear");
box->addItem(L"Anisotropic");
box->addItem(L"Isotropic");
// disabilito alpha
for (s32 i=0; i<gui::EGDC_COUNT ; ++i)
{
video::SColor col = env->getSkin()>getColor((gui::EGUI_DEFAULT_COLOR)i);
col.setAlpha(255);
env->getSkin()->setColor((gui::EGUI_DEFAULT_COLOR)i, col);
}
// aggiungo la tabcontrol
createToolBox();
IGUIStaticText* fpstext = env->addStaticText(L"",
core::rect<s32>(400,4,570,23), true, false, bar);
IGUIStaticText* postext = env->addStaticText(L"",
core::rect<s32>(10,50,470,80),false, false, 0, GUI_ID_POSITION_TEXT);
postext->setVisible(false);
// setto il titolo della window
IrrLicht
um 97 di 116
Caption += " - [";
Caption += driver->getName();
Caption += "]";
Device->setWindowCaption(Caption.c_str());
// visualizzo Informazioni su
if (argc==1)showAboutText();
loadModel(StartUpModelFile.c_str());
SkyBox = smgr->addSkyBoxSceneNode(
driver->getTexture("irrlicht2_up.jpg"),
driver->getTexture("irrlicht2_dn.jpg"),
driver->getTexture("irrlicht2_lf.jpg"),
driver->getTexture("irrlicht2_rt.jpg"),
driver->getTexture("irrlicht2_ft.jpg"),
driver->getTexture("irrlicht2_bk.jpg"));
// camera scene node
Camera[0] = smgr->addCameraSceneNodeMaya();
Camera[0]->setFarValue(20000.f);
// Maya camera
Camera[0]->setTarget(core::vector3df(0,30,0));
Camera[1] = smgr->addCameraSceneNodeFPS();
Camera[1]->setFarValue(20000.f);
Camera[1]->setPosition(core::vector3df(0,0,-70));
Camera[1]->setTarget(core::vector3df(0,30,0));
setActiveCamera(Camera[0]);
IGUIImage *img =
env->addImage(driver->getTexture("42.jpg"),
core::position2d<s32>(10, driver->getScreenSize().Height - 128));
// logo in basso a sinistra
img->setAlignment(EGUIA_UPPERLEFT,
EGUIA_UPPERLEFT,EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT);
while(Device->run() && driver)
{
if (Device->isWindowActive())
{
driver->beginScene(true, true, video::SColor(150,50,50,50));
smgr->drawAll();
env->drawAll();
driver->endScene();
core::stringw str(L"FPS: ");
str.append(core::stringw(driver->getFPS()));
str += L" Tris: ";
str.append(core::stringw(driver->getPrimitiveCountDrawn()));
fpstext->setText(str.c_str());
scene::ICameraSceneNode* cam = Device->getSceneManager()->
getActiveCamera();
str = L"Pos: ";
str.append(core::stringw(cam->getPosition().X));
str += L" ";
str.append(core::stringw(cam->getPosition().Y));
str += L" ";
str.append(core::stringw(cam->getPosition().Z));
str += L" Tgt: ";
str.append(core::stringw(cam->getTarget().X));
str += L" ";
str.append(core::stringw(cam->getTarget().Y));
str += L" ";
IrrLicht
um 98 di 116
str.append(core::stringw(cam->getTarget().Z));
postext->setText(str.c_str());
}
else
Device->yield();
}
Device->drop();
return (0);
}
IrrLicht
um 99 di 116
PER PIXEL LIGHTING
INTRODUZIONE
Nel progetto Visual Studio ci sono i seguenti file.
All’interno della cartella del progetto creare le sotto cartelle LIBRERIE e MEDIA.
File MAIN.CPP
#include <I:\irrlicht-1.6.1\irrlicht-1.6.1\include\irrlicht.h>
#include <iostream>
using namespace irr;
using namespace video;
using namespace scene;
using namespace core;
using namespace video;
using namespace io;
using namespace gui;
class MyEventReceiver : public IEventReceiver
{ public:
MyEventReceiver(scene::ISceneNode* room,
gui::IGUIEnvironment* env, video::IVideoDriver* driver)
{
// memorizzo il pointer alla stanza
Room = room;
Driver = driver;
// setto il font
gui::IGUISkin* skin = env->getSkin();
gui::IGUIFont* font = env->getFont("Media/fonthaettenschweiler.bmp");
if (font) skin->setFont(font);
// aggiungo la window ae la listbox
gui::IGUIWindow* window = env->addWindow(
core::rect<s32>(460,375,630,470), false, L"Usa 'E' + 'R' per cambiare");
ListBox = env->addListBox(core::rect<s32>(2,22,165,88), window);
ListBox->addItem(L"Diffuse");
IrrLicht
um 100 di 116
ListBox->addItem(L"Bump mapping");
ListBox->addItem(L"Parallax mapping");
ListBox->setSelected(1);
// creo il testo del problema
ProblemText = env->addStaticText(L"L'hardware non è abilitato.",
core::rect<s32>(150,20,470,80));
ProblemText->setOverrideColor(video::SColor(100,255,255,255));
// setto start material (parallax mapping se disponibile)
video::IMaterialRenderer* renderer =
Driver->getMaterialRenderer(video::EMT_PARALLAX_MAP_SOLID);
if (renderer && renderer->getRenderCapability() == 0) ListBox>setSelected(2);
setMaterial();
}
bool OnEvent(const SEvent& event)
{
// controllo se l'utente ha premuto 'E' or 'R'
if (event.EventType == irr::EET_KEY_INPUT_EVENT &&
!event.KeyInput.PressedDown && Room && ListBox)
{
int sel = ListBox->getSelected();
if (event.KeyInput.Key == irr::KEY_KEY_R) ++sel;
else
if (event.KeyInput.Key == irr::KEY_KEY_E) --sel;
else return false;
if (sel > 2) sel = 0;
if (sel < 0) sel = 2;
ListBox->setSelected(sel);
setMaterial();
}
return false;
}
private:
void setMaterial()
{
video::E_MATERIAL_TYPE type = video::EMT_SOLID;
switch(ListBox->getSelected())
{case 0: type = video::EMT_SOLID;
break;
case 1: type = video::EMT_NORMAL_MAP_SOLID;
break;
case 2: type = video::EMT_PARALLAX_MAP_SOLID;
break;
}
Room->setMaterialType(type);
video::IMaterialRenderer* renderer = Driver->getMaterialRenderer(type);
// visualizzo il testo del problema
if (!renderer || renderer->getRenderCapability() != 0)
ProblemText->setVisible(true);
else ProblemText->setVisible(false);
}
private:
gui::IGUIStaticText* ProblemText;
gui::IGUIListBox* ListBox;
scene::ISceneNode* Room;
video::IVideoDriver* Driver;
};
IrrLicht
um 101 di 116
int main(void)
{
// setup iniziali
IrrlichtDevice *device =
createDevice(EDT_DIRECT3D9, dimension2d<u32>(640, 480), 16, false);
if (device == 0) return 1; // impossibile creare il driver
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
gui::IGUIEnvironment* env = device->getGUIEnvironment();
driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, true);
// aggiungo il logo
env->addImage(driver->getTexture("Media/42.jpg"),
core::position2d<s32>(10,10));
scene::ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS();
camera->setPosition(core::vector3df(-200,200,-200));
device->getCursorControl()->setVisible(false);
driver->setFog(video::SColor(0,138,125,81), video::EFT_FOG_LINEAR, 250, 1000,
.003f, true, false);
scene::IAnimatedMesh* roomMesh = smgr->getMesh("Media/room.3ds");
scene::ISceneNode* room = 0;
if (roomMesh)
{
smgr->getMeshManipulator()->makePlanarTextureMapping(
roomMesh->getMesh(0), 0.003f);
video::ITexture* normalMap =
driver->getTexture("Media/rockwall_height.bmp");
if (normalMap) driver->makeNormalMapTexture(normalMap, 9.0f);
scene::IMesh* tangentMesh = smgr->getMeshManipulator()>createMeshWithTangents(
roomMesh->getMesh(0));
room = smgr->addMeshSceneNode(tangentMesh);
room->setMaterialTexture(0,
driver->getTexture("Media/rockwall.jpg"));
room->setMaterialTexture(1, normalMap);
room->getMaterial(0).SpecularColor.set(0,0,0,0);
room->setMaterialFlag(video::EMF_FOG_ENABLE, true);
room->setMaterialType(video::EMT_PARALLAX_MAP_SOLID);
// aggiusto l'altezza per l'effetto parallelo
room->getMaterial(0).MaterialTypeParam = 0.035f;
tangentMesh->drop();
}
// aggiungo la sfera earth
scene::IAnimatedMesh* earthMesh = smgr->getMesh("Media/earth.x");
if (earthMesh)
{
scene::IMeshManipulator *manipulator = smgr->getMeshManipulator();
scene::IMesh* tangentSphereMesh =
manipulator->createMeshWithTangents(earthMesh->getMesh(0));
// setto il valore alpha di tutti i vertici
manipulator->setVertexColorAlpha(tangentSphereMesh, 200);
core::matrix4 m;
m.setScale ( core::vector3df(50,50,50) );
manipulator->transformMesh( tangentSphereMesh, m );
scene::ISceneNode *sphere = smgr>addMeshSceneNode(tangentSphereMesh);
sphere->setPosition(core::vector3df(-70,130,45));
// carico la heightmap
IrrLicht
um 102 di 116
video::ITexture* earthNormalMap = driver>getTexture("Media/earthbump.jpg");
if (earthNormalMap)
{
driver->makeNormalMapTexture(earthNormalMap, 20.0f);
sphere->setMaterialTexture(1, earthNormalMap);
sphere>setMaterialType(video::EMT_NORMAL_MAP_TRANSPARENT_VERTEX_ALPHA);
}
sphere->setMaterialFlag(video::EMF_FOG_ENABLE, true);
// aggiungo la rotazione
scene::ISceneNodeAnimator* anim =
smgr->createRotationAnimator(core::vector3df(0,0.1f,0));
sphere->addAnimator(anim);
anim->drop();
tangentSphereMesh->drop();
}
// aggiungo light 1
scene::ILightSceneNode* light1 =
smgr->addLightSceneNode(0, core::vector3df(0,0,0),
video::SColorf(0.5f, 1.0f, 0.5f, 0.0f), 800.0f);
light1->setDebugDataVisible ( scene::EDS_BBOX );
// aggiungo fly circle animator per light 1
scene::ISceneNodeAnimator* anim =
smgr->createFlyCircleAnimator (core::vector3df(50,300,0),190.0f, -0.003f);
light1->addAnimator(anim);
anim->drop();
// attacco la billboard alla light
scene::ISceneNode* bill =
smgr->addBillboardSceneNode(light1, core::dimension2d<f32>(60, 60));
bill->setMaterialFlag(video::EMF_LIGHTING, false);
bill->setMaterialFlag(video::EMF_ZWRITE_ENABLE, false);
bill->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
bill->setMaterialTexture(0, driver->getTexture("Media/particlered.bmp"));
// aggiungo light 2
scene::ISceneNode* light2 =
smgr->addLightSceneNode(0, core::vector3df(0,0,0),
video::SColorf(1.0f, 0.2f, 0.2f, 0.0f), 800.0f);
// aggiungo fly circle animator per light 2
anim = smgr->createFlyCircleAnimator(core::vector3df(0,150,0), 200.0f,
0.001f, core::vector3df(0.2f, 0.9f, 0.f));
light2->addAnimator(anim);
anim->drop();
// attacco la billboard alla light
bill = smgr->addBillboardSceneNode(light2, core::dimension2d<f32>(120, 120));
bill->setMaterialFlag(video::EMF_LIGHTING, false);
bill->setMaterialFlag(video::EMF_ZWRITE_ENABLE, false);
bill->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
bill->setMaterialTexture(0, driver->getTexture("Media/particlewhite.bmp"));
// aggiungo il sistema particella
scene::IParticleSystemSceneNode* ps =
smgr->addParticleSystemSceneNode(false, light2);
scene::IParticleEmitter* em = ps->createBoxEmitter(
core::aabbox3d<f32>(-3,0,-3,3,1,3),
core::vector3df(0.0f,0.03f,0.0f),
IrrLicht
um 103 di 116
80,100,
video::SColor(0,255,255,255), video::SColor(0,255,255,255),
400,1100);
em->setMinStartSize(core::dimension2d<f32>(30.0f, 40.0f));
em->setMaxStartSize(core::dimension2d<f32>(30.0f, 40.0f));
ps->setEmitter(em);
em->drop();
scene::IParticleAffector* paf = ps->createFadeOutParticleAffector();
ps->addAffector(paf);
paf->drop();
ps->setMaterialFlag(video::EMF_LIGHTING, false);
ps->setMaterialFlag(video::EMF_ZWRITE_ENABLE, false);
ps->setMaterialTexture(0, driver->getTexture("Media/fireball.bmp"));
ps->setMaterialType(video::EMT_TRANSPARENT_VERTEX_ALPHA);
MyEventReceiver receiver(room, env, driver);
device->setEventReceiver(&receiver);
int lastFPS = -1;
while(device->run())
if (device->isWindowActive())
{
driver->beginScene(true, true, 0);
smgr->drawAll();
env->drawAll();
driver->endScene();
int fps = driver->getFPS();
if (lastFPS != fps)
{
core::stringw str = L"Per pixel lighting. Esempio [";
str += driver->getName();
str += "] FPS:";
str += fps;
device->setWindowCaption(str.c_str());
lastFPS = fps;
}
}
device->drop();
return (0);
}
IrrLicht
um 104 di 116
IrrLicht
um 105 di 116
VERTEX E PIXEL SHADER
INTRODUZIONE
Nel progetto Visual Studio ci sono i seguenti file.
All’interno della cartella del progetto creare le sotto cartelle LIBRERIE e MEDIA.
File MAIN.CPP
#include <I:\irrlicht-1.6.1\irrlicht-1.6.1\include\irrlicht.h>
#include <iostream>
using namespace irr;
using namespace video;
using namespace scene;
using namespace core;
using namespace video;
using namespace io;
using namespace gui;
IrrlichtDevice* device = 0;
bool UseHighLevelShaders = false;
class MyShaderCallBack : public video::IShaderConstantSetCallBack
{ public:
virtual void OnSetConstants(video::IMaterialRendererServices* services,
s32 userData)
{video::IVideoDriver* driver = services->getVideoDriver();
core::matrix4 invWorld = driver->getTransform(video::ETS_WORLD);
invWorld.makeInverse();
if (UseHighLevelShaders)
services->setVertexShaderConstant("mInvWorld", invWorld.pointer(), 16);
else
IrrLicht
um 106 di 116
services->setVertexShaderConstant(invWorld.pointer(), 0, 4);
core::matrix4 worldViewProj;
worldViewProj = driver->getTransform(video::ETS_PROJECTION);
worldViewProj *= driver->getTransform(video::ETS_VIEW);
worldViewProj *= driver->getTransform(video::ETS_WORLD);
if (UseHighLevelShaders)
services->setVertexShaderConstant("mWorldViewProj", worldViewProj.pointer(), 16);
else
services->setVertexShaderConstant(worldViewProj.pointer(), 4, 4);
// setto la posizione della camera
core::vector3df pos = device->getSceneManager()->
getActiveCamera()->getAbsolutePosition();
if (UseHighLevelShaders)
services->setVertexShaderConstant("mLightPos",
reinterpret_cast<f32*>(&pos), 3);
else
services->setVertexShaderConstant(reinterpret_cast<f32*>(&pos), 8, 1);
// setto light color
video::SColorf col(0.0f,1.0f,1.0f,0.0f);
if (UseHighLevelShaders)
services->setVertexShaderConstant("mLightColor",
reinterpret_cast<f32*>(&col), 4);
else
services->setVertexShaderConstant(reinterpret_cast<f32*>(&col), 9, 1);
core::matrix4 world = driver->getTransform(video::ETS_WORLD);
world = world.getTransposed();
if (UseHighLevelShaders)
services->setVertexShaderConstant("mTransWorld", world.pointer(), 16);
else
services->setVertexShaderConstant(world.pointer(), 10, 4);
}
};
int main(void)
{
// seleziono il driver type
video::E_DRIVER_TYPE driverType = video::EDT_DIRECT3D9;
printf("\n\nSeleziona il driver:\n\n"\
" (a) Direct3D 9.0c\n (b) Direct3D 8.1\n (c) OpenGL 1.5\n"\
" (d) Software Renderer\n (e) Burning's Software Renderer\n"\
" (f) NullDevice\n (Qualsiasi altro tasto) Uscita ");
char i;
std::cin >> i;
switch(i)
{
case 'a': driverType = video::EDT_DIRECT3D9;break;
case 'b': driverType = video::EDT_DIRECT3D8;break;
case 'c': driverType = video::EDT_OPENGL; break;
case 'd': driverType = video::EDT_SOFTWARE; break;
case 'e': driverType = video::EDT_BURNINGSVIDEO;break;
case 'f': driverType = video::EDT_NULL; break;
default: return 1;
}
// usare high level shaders per questo esempio
if (driverType == video::EDT_DIRECT3D9 ||
driverType == video::EDT_OPENGL)
{
printf("\n\nPer favore premi 'y' se desideri usare high level shaders: ");
IrrLicht
um 107 di 116
std::cin >> i;
if (i == 'y') UseHighLevelShaders = true;
}
device = createDevice(driverType, core::dimension2d<u32>(640, 480));
if (device == 0)return 1;
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
gui::IGUIEnvironment* gui = device->getGUIEnvironment();
io::path vsFileName; // filename per il vertex shader
io::path psFileName; // filename per il pixel shader
switch(driverType)
{
case video::EDT_DIRECT3D8:
psFileName = "Media/d3d8.psh";
vsFileName = "Media/d3d8.vsh";
break;
case video::EDT_DIRECT3D9:
if (UseHighLevelShaders)
{
psFileName = "Media/d3d9.hlsl";
vsFileName = psFileName;
}
else
{
psFileName = "Media/d3d9.psh";
vsFileName = "Media/d3d9.vsh";
}
break;
case video::EDT_OPENGL:
if (UseHighLevelShaders)
{
psFileName = "Media/opengl.frag";
vsFileName = "Media/opengl.vert";
}
else
{
psFileName = "Media/opengl.psh";
vsFileName = "mMdia/opengl.vsh";
}
break;
}
if (!driver->queryFeature(video::EVDF_PIXEL_SHADER_1_1) &&
!driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1))
{
device->getLogger()->log("ATTENZIONE: Pixel shaders disabilitato "\
"perchè manca il supporto driver/hardware.");
psFileName = "";
}
if (!driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1) &&
!driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1))
{
device->getLogger()->log("ATTENZIONE: Vertex shaders disabilitato "\
"perchè manca il supporto driver/hardware.");
vsFileName = "";
}
video::IGPUProgrammingServices* gpu = driver->getGPUProgrammingServices();
s32 newMaterialType1 = 0;
s32 newMaterialType2 = 0;
if (gpu)
{
MyShaderCallBack* mc = new MyShaderCallBack();
IrrLicht
um 108 di 116
if (UseHighLevelShaders)
{
// creo materiale dallo shaders di alto livello(hlsl o glsl)
newMaterialType1 = gpu->addHighLevelShaderMaterialFromFiles(
vsFileName, "vertexMain", video::EVST_VS_1_1,
psFileName, "pixelMain", video::EPST_PS_1_1,
mc, video::EMT_SOLID);
newMaterialType2 = gpu->addHighLevelShaderMaterialFromFiles(
vsFileName, "vertexMain", video::EVST_VS_1_1,
psFileName, "pixelMain", video::EPST_PS_1_1,
mc, video::EMT_TRANSPARENT_ADD_COLOR);
}
else
{
// creo material dallo shaders di basso livello (asm or arb_asm)
newMaterialType1 = gpu->addShaderMaterialFromFiles(vsFileName,
psFileName, mc, video::EMT_SOLID);
newMaterialType2 = gpu->addShaderMaterialFromFiles(vsFileName,
psFileName, mc, video::EMT_TRANSPARENT_ADD_COLOR);
}
mc->drop();
}
scene::ISceneNode* node = smgr->addCubeSceneNode(50);
node->setPosition(core::vector3df(0,0,0));
node->setMaterialTexture(0, driver->getTexture("media/wall.bmp"));
node->setMaterialFlag(video::EMF_LIGHTING, false);
node->setMaterialType((video::E_MATERIAL_TYPE)newMaterialType1);
smgr->addTextSceneNode(gui->getBuiltInFont(),
L"PS & VS & EMT_SOLID",
video::SColor(255,255,255,255), node);
scene::ISceneNodeAnimator* anim = smgr->createRotationAnimator(
core::vector3df(0,0.3f,0));
node->addAnimator(anim);
anim->drop();
node = smgr->addCubeSceneNode(50);
node->setPosition(core::vector3df(0,-10,50));
node->setMaterialTexture(0, driver->getTexture("Media/wall.bmp"));
node->setMaterialFlag(video::EMF_LIGHTING, false);
node->setMaterialType((video::E_MATERIAL_TYPE)newMaterialType2);
smgr->addTextSceneNode(gui->getBuiltInFont(),
L"PS & VS & EMT_TRANSPARENT",
video::SColor(255,255,255,255), node);
anim = smgr->createRotationAnimator(core::vector3df(0,0.3f,0));
node->addAnimator(anim);
anim->drop();
node = smgr->addCubeSceneNode(50);
node->setPosition(core::vector3df(0,50,25));
node->setMaterialTexture(0, driver->getTexture("Media/wall.bmp"));
node->setMaterialFlag(video::EMF_LIGHTING, false);
smgr->addTextSceneNode(gui->getBuiltInFont(), L"NO SHADER",
video::SColor(255,255,255,255), node);
driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
smgr->addSkyBoxSceneNode(
driver->getTexture("Media/irrlicht2_up.jpg"),
driver->getTexture("Media/irrlicht2_dn.jpg"),
driver->getTexture("Media/irrlicht2_lf.jpg"),
IrrLicht
um 109 di 116
driver->getTexture("Media/irrlicht2_rt.jpg"),
driver->getTexture("Media/irrlicht2_ft.jpg"),
driver->getTexture("Media/irrlicht2_bk.jpg"));
driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true);
scene::ICameraSceneNode* cam = smgr->addCameraSceneNodeFPS();
cam->setPosition(core::vector3df(-100,50,100));
cam->setTarget(core::vector3df(0,0,0));
device->getCursorControl()->setVisible(false);
int lastFPS = -1;
while(device->run())
if (device->isWindowActive())
{
driver->beginScene(true, true, video::SColor(255,0,0,0));
smgr->drawAll();
driver->endScene();
int fps = driver->getFPS();
if (lastFPS != fps)
{
core::stringw str = L"Vertex e pixel shader. Esempio [";
str += driver->getName();
str += "] FPS:";
str += fps;
device->setWindowCaption(str.c_str());
lastFPS = fps;
}
}
device->drop();
return (0);
}
IrrLicht
um 110 di 116
IrrLicht
um 111 di 116
PRIMA APPLICAZIONE VISUAL C#
INTRODUZIONE
Nel progetto Visual Studio ci sono i seguenti file.
All’interno della cartella del progetto creare la sotto cartella MEDIA.
File IRRLICHT.NET.DLL
Nella finestra Esplora soluzioni, fare clic con il pulsante destro su Riferimenti/Aggiungi
riferimento….
File IRRLICHT.DLL
Fare clic con il pulsante destro su Prima Applicazione/Aggiungi/Elemento esistente…,
quindi selezionare IRRLICHT.DLL e nella finestra Proprietà selezionare le voci
visualizzate in figura.
File EXAMPLE.CS
using System;
using Irrlicht;
using Irrlicht.Video;
IrrLicht
um 112 di 116
using Irrlicht.Core;
using Irrlicht.Scene;
namespace PrimaApplicazione {
class Example {
[STAThread]
static void Main(string[] args)
{
// setup iniziali
IrrlichtDevice device = new IrrlichtDevice(DriverType.OPENGL);
device.ResizeAble = true;
device.WindowCaption = "Ciao, mondo in HirrLicht";
// carica le texture
ITexture texSydney = device.VideoDriver.GetTexture(@"Media\sydney.bmp");
ITexture texWall = device.VideoDriver.GetTexture(@"Media\wall.bmp");
ITexture texLogo = device.VideoDriver.GetTexture(@"Media\42.jpg");
// carica la mesh animata
Irrlicht.Scene.IAnimatedMesh mesh =
device.SceneManager.GetMesh(@"Media\sydney.md2");
if (mesh == null)
{ System.Windows.Forms.MessageBox.Show(@"Non riesco a caricare la mesh");
return;
}
// aggiungo la videocamera
ICameraSceneNode cam =
device.SceneManager.AddCameraSceneNodeFPS(null, 100, 100, -1);
cam.Position = new Vector3D(20,0,-50);
ISceneNode node = device.SceneManager.AddTestSceneNode(15,
null, -1, new Vector3D(30,-15,0));
node.SetMaterialTexture(0, texWall);
node = device.SceneManager.AddAnimatedMeshSceneNode(mesh, null, -1);
node.SetMaterialTexture(0, texSydney);
node.SetMaterialFlag(MaterialFlag.LIGHTING, false);
// elimino il cursore del mouse
device.CursorControl.Visible = false;
int fps = 0;
// ciclo principale
while(device.Run())
{
if (device.WindowActive)
{
device.VideoDriver.BeginScene(true, true, new Color(0,100,100,100));
device.SceneManager.DrawAll();
// disegno il logo
device.VideoDriver.Draw2DImage(
texLogo, new Position2D(10,10),
new Rect(0, 0, 96, 64),
new Rect(new Position2D(0,0),
device.VideoDriver.ScreenSize),
new Color(0xffffff), false);
device.VideoDriver.EndScene();
if (fps != device.VideoDriver.FPS)
{
fps = device.VideoDriver.FPS;
device.WindowCaption = "Ciao, mondo in HirrLicht [" +
device.VideoDriver.Name + "] fps:" + fps;
}
}
}
IrrLicht
um 113 di 116
}
}
}
IrrLicht
um 114 di 116
PRIMA APPLICAZIONE VISUAL BASIC
INTRODUZIONE
Nel progetto Visual Studio ci sono i seguenti file.
File MODULE1.VB
Imports Irrlicht
Imports Irrlicht.Video
Imports Irrlicht.Core
Imports Irrlicht.Scene
Module Module1
Sub Main()
‘ setup iniziali
Dim device As New IrrlichtDevice(Irrlicht.Video.DriverType.OPENGL)
device.ResizeAble = True
device.WindowCaption = "Ciao, mondo in HirrLicht "
‘ carica le texture
Dim texSydney As ITexture = device.VideoDriver.GetTexture("Media\sydney.bmp")
Dim texWall As ITexture = device.VideoDriver.GetTexture("Media\wall.bmp")
Dim texLogo As ITexture = device.VideoDriver.GetTexture("Media\42.jpg")
IrrLicht
um 115 di 116
‘ carica la mesh animata
Dim mesh As Irrlicht.Scene.IAnimatedMesh =
device.SceneManager.GetMesh("Media\sydney.md2")
If mesh Is Nothing Then
System.Windows.Forms.MessageBox.Show("Non riesco a caricare la mesh")
Return
End If
‘ aggiungo la videocamera
Dim cam As ICameraSceneNode =
device.SceneManager.AddCameraSceneNodeFPS(Nothing, 100, 100, -1)
cam.Position = New Vector3D(20, 0, -50)
Dim node As ISceneNode = device.SceneManager.AddTestSceneNode(15,
Nothing, -1, New Vector3D(30, -15, 0)) node.SetMaterialTexture(0, texWall)
node = device.SceneManager.AddAnimatedMeshSceneNode(mesh, Nothing, -1)
node.SetMaterialTexture(0, texSydney)
node.SetMaterialFlag(MaterialFlag.LIGHTING, False)
‘ elimino il cursore del mouse
device.CursorControl.Visible = False
Dim fps As Integer = 0
‘ ciclo principale
While device.Run() = True
If device.WindowActive Then
device.VideoDriver.BeginScene(True, True, New Color(0, 100, 100, 100))
device.SceneManager.DrawAll()
‘ disegno il logo
device.VideoDriver.Draw2DImage(texLogo, New Position2D(10, 10), New Rect(0, 0, 96,
64), New Rect(New Position2D(0, 0), device.VideoDriver.ScreenSize), New Color(255,
255, 255, 255), False)
device.VideoDriver.EndScene()
If (fps <> device.VideoDriver.FPS) Then
fps = device.VideoDriver.FPS
device.WindowCaption = " Ciao, mondo in HirrLicht [" +
device.VideoDriver.Name + "] fps:" + fps.ToString
End If
End If
End While
End Sub
End Module
IrrLicht
um 116 di 116
UBERTINI MASSIMO
http://www.ubertini.it
[email protected]
Dip. Informatica Industriale
I.T.I.S. "Giacomo Fauser"
Via Ricci, 14
28100 Novara Italy
tel. +39 0321482411
fax +39 0321482444
http://www.fauser.edu
[email protected]