• Benvenuto in Making Videogames!
  • Condividi la tua passione!
  • Crea il tuo Videogioco!
Benvenuto ospite! Login Registrati



Valutazione discussione:
  • 1 voto(i) - 5 media
  • 1
  • 2
  • 3
  • 4
  • 5
Title: Tutorial 3 - Custom SceneNode
Modi discussione
#1
Riprendo questa serie di traduzioni dei tutorials di Irrlicht, ogni tanto aggiungerò qualche mio appunto e osservazione tanto per rendere più chiaro cosa accade.

Tutorial 3: Custom SceneNode

[Immagine: 003shot.jpg]

Questo tutorial è più avanzato dei precedenti. Se siete agli inizi dello studio di Irrlicht potete saltare questo capitolo e proseguire con altri esempi. Questo tutorial mostra come si creano nodi per scene personalizzate (custom scene node) e come si usano nell'engine. Un custom node scene si renderà necessario tutte le volte che vorrete implementare una etcnica di render della scena che non è al momento supportata da irrlicht. Ad esempio potreste voler creare un renderer specifico per gestire scene indoor (come la tecnica dei portali) oppure un renderer avanzato per la gestione dei terreni e degli spazi aperti. Quindi creare un custon scene node serve ad estendere l'engine di Irrlicht per i nostri scopi specifici.
Il tutorial sarà volutamente semplice e breve, tutto compreso in un singolo .cpp, utilizzando l'engine con le stesse metodologie già usate negli altri tutorials.
Per cominciare vediamo il file header, qui scegliamo di usare il namespace irr per non dover indicare tutte le classi dell'engine con l'intero namespace ogni volta. Poi diciamo al linker di agganciare il file .lib dell'engine.
Codice PHP:
#include <irrlicht.h>
#include "driverChoice.h"

using namespace irr;

#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif 
Ora viene la parte difficile del tutorial: la classe del nostro personale custom scene node. Per semplicità non ci metteremo a scrivere un render di portali indoor o un sistema di terrain ma creeremo un renderer personalizzato che disegnerà un tetraedro, un oggetto 3d con 4 vertici connessi (in pratica una piramide con base triangolare). In realtà non serve un custon scene node per fare questo; potremmo invece creare una mesh e passarla al gestore principale della scena irr:Confusedcene::IMeshSceneNode.
Ma noi vogliamo proprio evitare questo, vogliamo infatti creare la nostra personale “ImeshSceneNode” e per farlo dovremo quindi derivare una classe dal livello superiore che è irr:Confusedcene::ISceneNode ; così facendo ogni classe nodo derivata da questa potrà essere inserito nella scena come già avviene per le “sorelle” già implementate in Irrlicht.
Ovviamente dovremo anche sovrascrivere (overrriding degli operatori) alcuni metodi della classe (altrimenti la staremmo solo clonano no?).
La classe la chiameremo CsampleSceneNode.
Codice PHP:
class CSampleSceneNode : public scene::ISceneNode

Ora dichiariamo alcune variabili membro: una box di contenimento (un aabbox3d) , una struttura per tenere i 4 vertici (un array di 4 vertici S3DVertex) e un materiale per colorare il nostro tetraedro (un SMaterial).
Codice PHP:
core::aabbox3d<f32Box;
    
video::S3DVertex Vertices[4];
    
video::SMaterial Material
Come parametri per il costruttore passiamo la scena nodo parente, un puntatore allo scene manager dell'engine e l'id della nostra scena nodo personalizzata. Una volta dentro al costruttore richiamiamo il costruttore della classe da cui deriviamo passandogli i parametri di prima, poi andiamo ad impostare alcune proprietà del nostro materiale (disabilitiamo il wireframe che di default è false comunque e disattiviamo l'illuminazione che invece di solito è true).
Fatto questo creiamo i 4 vertici posizionandoli a (0,0,10)(10,0,-10) (0,20,0) (-10,0,-10) rispetto alla propria origine, ciascun vertice prende le coordinate delle normali nx,ny,nz opportune (1,1,0) (1,0,0) (0,1,1) (0,0,1); quindi segue il colore di ciascun vertice (255,0,255,255) (255,255,0,255) (255,255,255,0) (255,0,255,0) e per finire la coordinata della texture rispetto all'ipotetico quadrato della texture (0,1) (1,1) (1,0) (0,0). Eccovi spiegata la riga del vertice [0] con i colori.
Codice PHP:
Vertices[0] = video::S3DVertex(0,0,101,1,0video::SColor(255,0,255,255), 01); 

Codice PHP:
public:
    
CSampleSceneNode(scene::ISceneNodeparentscene::ISceneManagermgrs32 id)
        : 
scene::ISceneNode(parentmgrid)
    {
    
Material.Wireframe false;
    
Material.Lighting false;
    
Vertices[0] = video::S3DVertex(0,0,101,1,0,video::SColor(255,0,255,255),0,1);
    
Vertices[1] = video::S3DVertex(10,0,-101,0,0,video::SColor(255,255,0,255),1,1);
    
Vertices[2] = video::S3DVertex(0,20,00,1,1,video::SColor(255,255,255,0),1,0);
    
Vertices[3] = video::S3DVertex(-10,0,-100,0,1,video::SColor(255,0,255,0),0,0); 

Poiché Irrlicht richiede di conoscere la box di contenimento di ciascun nodo scena per utilizzare il culling integrato e altre automatismi ecco che andiamo a costruire la bounding box del tetraedro utilizzando proprio le coordinate dei 4 vertici che lo compongono.
Se comunque non voleste creare da voi la bounding box allora potrete sempre utilizzare irr:Confusedcene::ISceneNode:ConfusedetAutomaticCulling() e irr:Confusedcene::EAC_OFF non siete comunque obbligati a fornirla.

Codice PHP:
Box.reset(Vertices[0].Pos);
        for (
s32 i=1i<4; ++i)
            
Box.addInternalPoint(Vertices[i].Pos);
    } 

Come si crea la boundign box? Cicliamo i 4 vertici da 0 a 3 e aggiungiamo i punti della box con box.addinternalpoint a cui va dato il vettore Pos che viene da ciascun vertice, verrà un cubo con ciascun angolo dato dalla massima coordinata di ognuno dei quattro vettori.
Prima del disegno vero e proprio in ogni nodo di una scena bisogna richiamare un particolare metodo che richiama a sua volta lo Scenemanager principale, irr:Confusedcene::ISceneNode::OnRegisterSceneNode() con questo metodo non si fa altro che registrasi nello scenemanager per dirgli che vorremo essere disegnati. Così lo scenemanager quando chiamerà irr:Confusedcene::ISceneNode::render() saprà che c'è anche il nostro tetraedro.
Per esempio, in una scena normale i nodi vengono disegnati uno dopo l'altro allo stesso livello, mentre invece lo stencil buffer che contiene le ombre è tipicamente disegnato alla fine. Al contrario luci e camere sono tra le prime ad essere gestite dallo scenemanager. In questo caso gegistreremo il nostro tetraedro come un modo da renderizzare normalmente (oltre il this non mettiamo altro perché è di DEFAULT). Se inevce avessimo voluto renderizzarlo come una luce o una camera allora il parametro da usare è registerNodeForRendering(this, SNRT_LIGHT_AND_CAMERA).
Fatto questo richiamiamo il metodo della classe principale che abbiamo appena sovrascritto (overriding) che è sempre irr:Confusedcene::ISceneNode::OnRegisterSceneNode() così il metodo registrerà tutti gli eventuali figli del nodo che stiamo registrando.
Codice PHP:
virtual void OnRegisterSceneNode()
    {
        if (
IsVisible)
            
SceneManager->registerNodeForRendering(this);
        
ISceneNode::OnRegisterSceneNode();
    } 

Nel metodo render() il nodo va ovviamente in rendering. Lo sovrascriviamo andando a disegnare il tetraetro.
Codice PHP:
virtual void render()
    {
        
u16 indices[] = {   0,2,32,1,31,0,32,0,1  };
        
video::IVideoDriverdriver SceneManager->getVideoDriver();
        
driver->setMaterial(Material);
        
driver->setTransform(video::ETS_WORLDAbsoluteTransformation);
        
driver->drawVertexPrimitiveList(&Vertices[0], 4, &indices[0], 4video::EVT_STANDARDscene::EPT_TRIANGLESvideo::EIT_16BIT);
    } 

Prendo 4 punti per il tetraedro, creo il driver da associare allo SceneManager, gli associo il materiale del tetraedro, impongo al driver le trasformazioni delle coordinate rispetto alle coordinate mondo e quindi finalmente disegno la primitiva fornendo vertici e triangoli del tetraedro.
Alla fine andiamo ad aggiungere tre metodi ulteriori irr:Confusedcene::ISceneNode::getBoundingBox() per restituire la boundingbox del nostro nodo, irr:Confusedcene::ISceneNode::getMaterialCount() che restituisce quanti materiali sono presenti nella scena del nostro nodo (solo uno) e poi il irr:Confusedcene::ISceneNode::getMaterial() che ritorna il materiale in base all'indice fornito. In questo caso sappiamo già di avere un solo materiale di indice 0 e restituiamo il solo che abbiamo qualsiasi indice fornito.

Codice PHP:
virtual const core::aabbox3d<f32>& getBoundingBox() const
    {
        return 
Box;
    }

    
virtual u32 getMaterialCount() const
    {
        return 
1;
    }


    
virtual video::SMaterialgetMaterial(u32 i)
    {
        return 
Material;
    }   
}; 

Fatto. La classe del nostro nodo di scena personalizzato è finito. Ora non resta che lanciare l'engine, creare il nostro nodo ed una camera per vedere il risultato.
Codice PHP:
int main()
{
    
// ask user for driver
    
video::E_DRIVER_TYPE driverType=driverChoiceConsole();
    if (
driverType==video::EDT_COUNT)
        return 
1;
    
// create device
    
IrrlichtDevice *device createDevice(driverType,
            
core::dimension2d<u32>(640480), 16false);
    if (
device == 0)
        return 
1// could not create selected driver.
    // create engine and camera
    
device->setWindowCaption(L"Custom Scene Node - Irrlicht Engine Demo");
    
video::IVideoDriverdriver device->getVideoDriver();
    
scene::ISceneManagersmgr device->getSceneManager();
    
smgr->addCameraSceneNode(0core::vector3df(0,-40,0), core::vector3df(0,0,0)); 
Ora è il momento di creare il nostro nodo. Non verifichimao il risultato della nostra chiamata sollevando una eccezione nel caso ritorni 0 (cioè if (node) {} ecc..) Il nuovo nodo crea se stesso restituendo un conteggio di riferimento 1 cui si unirà il riferimento che viene dal nodo padre anch'esso aggiunto alla scena. Poiché dopo dovrò droppare questo riferimento lo farò solo dopo aver finito di usare il mio nodo, senza preoccuparmi di quale sia l'indice di riferimento raggiunto.
Codice PHP:
CSampleSceneNode *myNode =
        new 
CSampleSceneNode(smgr->getRootSceneNode(), smgr666); 

Per animare un po' la scena diamo una rotazione al nostro tetraedro anche per dimostrarne l'uso come un qualsiasi altro nodo di scena. Aggiungiamo un animator (anim) al nodo che ruoti un po' il tetraedro, lo facciamo usando irr:Confusedcene::ISceneManager::createRotationAnimator() che stavolta restituisce 0, quindi possiamo checkarne il valore di ritorno, aggiungerlo alla scena e quindi dropparlo come sempre.
Codice PHP:
scene::ISceneNodeAnimatoranim =
        
smgr->createRotationAnimator(core::vector3df(0.8f00.8f));
    if(
anim)
    {
        
myNode->addAnimator(anim); 
Infatti una volta fatto riferimento alla nostra anim e aggiundo al nostro nodo, possiamo usare irr::IReferenceCounted::drop() per dropparla. E come sempre azzeriamo il suo riferimento con 0 in modo da non utilizzarlo più.
Codice PHP:
anim->drop();
        
anim 0;
    } 
Ora che ho veramente finito con il mio nuovo nodo personalizzato (myNode) posso dropparlo e lo farò sempre con drop() e ponendo il suo riferimento a 0. Questo non cancellerà effettivamente il mio oggetto nodo perché è ancorato all'albero di scena e ciò ne impedisce la cancellazioni a meno che non si cancelli l'intera scena o non si tolga il nodo dalla stessa.
Codice PHP:
myNode->drop();
    
myNode 0// As I shouldn't refer to it again, ensure that I can't 
Ora andiamo a disegnare tutto e terminiamo.
Codice PHP:
u32 frames=0;
    while(
device->run())
    {
        
driver->beginScene(truetruevideo::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;

Fine. Compilate e provate il programma.

(Per rispettare l'aspetto dei tutorials i prossimi tutorials saranno fatti solo su pdf, scusatemi ma il code del forum è parecchio limitato per questo genere di cose)

Link al tutorial in pdf QUI
 
Reply
  


Vai al forum:


Browsing: 1 Ospite(i)