• Benvenuto in Making Videogames!
  • Dai sfogo alla tua fantasia!
  • Crea il tuo Videogioco!
Benvenuto ospite! Login Registrati




Valutazione discussione:
  • 1 voto(i) - 5 media
  • 1
  • 2
  • 3
  • 4
  • 5
Tutorial 20 - Managed Lights
#1
Tutorial 20: Managed Lights

[Immagine: 020shot.jpg]

Scritto da Colin MacDonald. Questo tutorial spiega l'uso del Light Manager di Irrlicht (gestore delle luci). Permette l'uso di più luci dinamiche rispetto a quante ne possa gestire l'hardware Non tratteremo per semplicità le ulteriori applicazioni per il Light Manager, come i nodi di scena per le callbacks.
Codice PHP:
#include <irrlicht.h>
#include "driverChoice.h"

using namespace irr;
using namespace core;

#if defined(_MSC_VER)
#pragma comment(lib, "Irrlicht.lib")
#endif // MSC_VER 
Normalmente abbiamo un limite di 8 luci dinamiche per scena: si tratta di un limite hardware. Se però volessimo usare più luci dinamiche nella scena possiamo farlo registrando un light manager pozionale che ci permette di spegnere e accendere le luci in determinati punti durante il rendering. Così facendo il limite delle 8 luci rimarrà sempre ma sarà superato per ciascuna scena.
Questa cosa è completamente opzionale: se non creassimo questo light manager, di default Irrlicht userebbe comunque uno schema basato sulla distanza che attiverebbe le luci hardware dando priorità a quelle più vicine.
Con NO_MANAGEMENT disabilitiamo il light manager per far si chee Irrlicht utilizzi la sua modalità di default per la gestione delle luci. Le 8 luci più vicine alla camera verrebbero accese, mentre le restanti verrebbero spente. Questo, nel nostro esempio, produrrà un comportamento curioso ma comunque incoerente.
LIGHTS_NEAREST_NODE mostra una implementazione dove si spengono solo un limitato numero di luci per nodo mesh di scena. Accende le 3 luci più vicine alla camera per il rendering ma spenge le restanti. La cosa funziona ma siccome lavoriamo su ogni singola luce, in caso di molte luci non scala di conseguenza. Il flickering (sfarfallio) che si può vedere è causato dal continuo scambio di luci che is accendono/spengono in base alla loro posizione rispetto ai cubi (si tratta di una dimostrazione voluta appositamente per dimostrare i limiti di questa tecnica).
LIGHTS_IN_ZONE mostra una tecnica basata su 'zone' intere. Ogni nodo di scena vuoto viene considerato come parente di una zona. Quando i nodi sono renderizzati, tutte le luci vengono spente, viene trovata la zona con cui è imparentata la nostra scena e vengono accese le luci che vi si trovano all'interno, e tutti i rispettivi discendenti nel grafo della scena. Questo genera un effetto più reale nella illuminazione di ciascun cubo del nostro esempio. Analogamente possiamo usare una simile per illuminare tutte le mesh che (ad esempio) si trovano in una stanza, senza considerare le luci che si trovano in altre stanze.
Nel nostro light manager gestiamo anche l'event receiver; non è necessario né raccomandato per una applicazione reale, è solo per semplificare l'esempio.
Codice PHP:
class CMyLightManager : public scene::ILightManager, public IEventReceiver
{
    
typedef enum
    
{
        
NO_MANAGEMENT,
        
LIGHTS_NEAREST_NODE,
        
LIGHTS_IN_ZONE
    
}
    
LightManagementMode;

    
LightManagementMode Mode;
    
LightManagementMode RequestedMode;

    
// Con questi dati otteniamo tutte le informazioni del light manager che
    // stiamo usando.
    
scene::ISceneManager SceneManager;
    
core::array<scene::ISceneNode*> * SceneLightList;
    
scene::E_SCENE_NODE_RENDER_PASS CurrentRenderPass;
    
scene::ISceneNode CurrentSceneNode;

public:
    
CMyLightManager(scene::ISceneManagersceneManager)
        : 
Mode(NO_MANAGEMENT), RequestedMode(NO_MANAGEMENT),
        
SceneManager(sceneManager), SceneLightList(0),
        
CurrentRenderPass(scene::ESNRP_NONE), CurrentSceneNode(0)
    { }
    
// Il gestore dell'input utente per cambiare le strategie del light management
    
bool OnEvent(const SEvent event)
    {
        
bool handled false;

        if (
event.EventType == irr::EET_KEY_INPUT_EVENT && event.KeyInput.PressedDown)
        {
            
handled true;
            switch(
event.KeyInput.Key)
            {
            case 
irr::KEY_KEY_1:
                
RequestedMode NO_MANAGEMENT;
                break;
            case 
irr::KEY_KEY_2:
                
RequestedMode LIGHTS_NEAREST_NODE;
                break;
            case 
irr::KEY_KEY_3:
                
RequestedMode LIGHTS_IN_ZONE;
                break;
            default:
                
handled false;
                break;
            }

            if(
NO_MANAGEMENT == RequestedMode)
                
SceneManager->setLightManager(0); // Show that it's safe to register the light manager
            
else
                
SceneManager->setLightManager(this);
        }

        return 
handled;
    }

    
// Viene richiamato prima che il primo nodo della scena venga renderizzato.
    
virtual void OnPreRender(core::array<scene::ISceneNode*> & lightList)
    {
        
// Aggiorniamo la modalità da applicare al renderer
        
Mode RequestedMode;

        
// Salviamo una lista delle luci modificabile prima del OnPostRender().
        
SceneLightList = &lightList;
    }

    
// Richiamato dopo che l'ultimo nodo di scena è stato renderizzato.
    
virtual void OnPostRender()
    {
        
// Finché spengiamo le luci con con ilg estore di eventi, giriamo lungo
        // tutte le luci per assicurarci che siano tutte gestite. Normalmente non 
        // occorrerebbe farlo se usassimo un light manager, ora però lo facciamo da 
        // noi.
        
for (u32 i 0SceneLightList->size(); i++)
            (*
SceneLightList)[i]->setVisible(true);
    }

    
virtual void OnRenderPassPreRender(scene::E_SCENE_NODE_RENDER_PASS renderPass)
    {
        
// Non c'è da fare nulla qua tranne ricordarmi quale render ho passato.
        
CurrentRenderPass renderPass;
    }

    
virtual void OnRenderPassPostRender(scene::E_SCENE_NODE_RENDER_PASS renderPass)
    {
        
// Illumino solo i corpi solidi, spengo le luci rimanenti.
        
if (scene::ESNRP_SOLID == renderPass)
        {
            for (
u32 i 0SceneLightList->size(); ++i)
                (*
SceneLightList)[i]->setVisible(false);
        }
    }

    
// Lo richiamiamo prima che la scena venga renderizzata
    
virtual void OnNodePreRender(scene::ISceneNodenode)
    {
        
CurrentSceneNode node;

        
// Questo light manager considera solo oggetti solidi, ma possiamo 
        // cambiarlo in base alle nostre necessità.
        
if (scene::ESNRP_SOLID != CurrentRenderPass)
            return;

        
// Infatti in questo esempio: voglio considerare solo i cubi della scena
        // Vogliamo l'illuminazione per (almeno) le i nodi mesh animati e non
        // presenti nella scena.
        
if (node->getType() != scene::ESNT_CUBE)
            return;

        if (
LIGHTS_NEAREST_NODE == Mode)
        {
            
// E' una implementazione un po' ingenua che privilegia tutte le luci
            // presenti nella scena in base alla vicinanza al nodo da illuminare.
            // Questo produce uno sfarfallio quando le luci orbitano vicino ai
             // cubi che sono vicini alle 'zone' da illuminare.
            
const vector3df nodePosition node->getAbsolutePosition();

            
// Ordino le luci in base alla loro distanza dal nodo che vado a 
            // renderizzare.
            
array<LightDistanceElementsortingArray;
            
sortingArray.reallocate(SceneLightList->size());

            
u32 i;
            for(
0SceneLightList->size(); ++i)
            {
                
scene::ISceneNodelightNode = (*SceneLightList)[i];
                const 
f64 distance lightNode->getAbsolutePosition().getDistanceFromSQ(nodePosition);
                
sortingArray.push_back(LightDistanceElement(lightNodedistance));
            }

            
sortingArray.sort();

            
// La lista ora è ordinata per vicinanza al nodo.
            // Accendo le tre luci più vicine e spengo le altre.
            
for(0sortingArray.size(); ++i)
                
sortingArray[i].node->setVisible(3);
        }
        else if(
LIGHTS_IN_ZONE == Mode)
        {
            
// Usiamo un nodo di scena vuoto per rappresentare la 'zona'.  Per   
            // ciascuna mesh solida, spengo le luci, trovo la 'zona' parente e
            // accendo le luci che si trovano nel nodo di quella scena.
            // Questo è un sistema generico che non necessita di una particolare 
            // conoscenza di come è organizzato il grafo delle scena.
            
for (u32 i 0SceneLightList->size(); ++i)
            {
                if ((*
SceneLightList)[i]->getType() != scene::ESNT_LIGHT)
                    continue;
                
scene::ILightSceneNodelightNode static_cast<scene::ILightSceneNode*>((*SceneLightList)[i]);
                
video::SLight lightData lightNode->getLightData();

                if (
video::ELT_DIRECTIONAL != lightData.Type)
                    
lightNode->setVisible(false);
            }

            
scene::ISceneNode parentZone findZone(node);
            if (
parentZone)
                
turnOnZoneLights(parentZone);
        }
    }

    
// Chiamato dopo che il nodo specidifico è stato renderizzato
    
virtual void OnNodePostRender(scene::ISceneNodenode)
    {
        
// Non serve altro light management dopo il rendering dei singoli nodi.
    
}

private:

    
// Trovo la il nodo di scena vuoto che è parente del nodo specificato
    
scene::ISceneNode findZone(scene::ISceneNode node)
    {
        if (!
node)
            return 
0;

        if (
node->getType() == scene::ESNT_EMPTY)
            return 
node;

        return 
findZone(node->getParent());
    }

    
// Accendo tutte le luci figlie (dirette o indirette) del nodo specificato.
    
void turnOnZoneLights(scene::ISceneNode node)
    {
        
core::list<scene::ISceneNode*> const & children node->getChildren();
        for (
core::list<scene::ISceneNode*>::ConstIterator child children.begin();
            
child != children.end(); ++child)
        {
            if ((*
child)->getType() == scene::ESNT_LIGHT)
                (*
child)->setVisible(true);
            else 
// Si assume che le luci non abbiano figli che sono luci
                
turnOnZoneLights(*child);
        }
    }

    
// Una classe utility per ordinare i nodi in base ad una distanza
    
class LightDistanceElement
    
{
    public:
        
LightDistanceElement() {};

        
LightDistanceElement(scene::ISceneNodenf64 d)
            : 
node(n), distance(d) { }

        
scene::ISceneNodenode;
        
f64 distance;

        
// Gli elementi con distanza minore sono ordinati in cima all'array
        
bool operator < (const LightDistanceElementother) const
        {
            return (
distance other.distance);
        }
    };
};
int main(int argumentCountchar argumentValues[])
{
    
// richiesta utente del driver
    
video::E_DRIVER_TYPE driverType=driverChoiceConsole();
    if (
driverType==video::EDT_COUNT)
        return 
1;

    
IrrlichtDevice *device createDevice(driverType,
            
dimension2d<u32>(640480), 32);

    if(!
device)
        return -
1;
    
f32 const lightRadius 60.f// Enough to reach the far side of each 'zone'
    
video::IVideoDriverdriver device->getVideoDriver();
    
scene::ISceneManagersmgr device->getSceneManager();
    
gui::IGUIEnvironmentguienv device->getGUIEnvironment();

    
gui::IGUISkinskin guienv->getSkin();
    if (
skin)
    {
        
skin->setColor(gui::EGDC_BUTTON_TEXTvideo::SColor(255255255255));
        
gui::IGUIFontfont guienv->getFont("../../media/fontlucida.png");
        if(
font)
            
skin->setFont(font);
    }

    
guienv->addStaticText(L"1 - No light management"core::rect<s32>(10,10,200,30));
    
guienv->addStaticText(L"2 - Closest 3 lights"core::rect<s32>(10,30,200,50));
    
guienv->addStaticText(L"3 - Lights in zone"core::rect<s32>(10,50,200,70)); 
Aggiungiamo altre 'zone'. Questa tecnica la possiamo usare per accendere singole stanze ad esempio.
Codice PHP:
for(f32 zoneX = -100.fzoneX <= 100.fzoneX += 50.f)
        for(
f32 zoneY = -60.fzoneY <= 60.fzoneY += 60.f)
        {
            
// Per fare la zona uso un nodo di scena vuoto.
            
scene::ISceneNode zoneRoot smgr->addEmptySceneNode();
            
zoneRoot->setPosition(vector3df(zoneXzoneY0));

            
// Ogni zona contiene un cubo rotante
            
scene::IMeshSceneNode node smgr->addCubeSceneNode(15zoneRoot);
            
scene::ISceneNodeAnimator rotation smgr->createRotationAnimator(vector3df(0.25f0.5f0.75f));
            
node->addAnimator(rotation);
            
rotation->drop();

            
// Ogni cubo ha tre luci agganciate. Le luci sono agganciate alla 
            // billboard così da vedere dove sono. Le billboards sono agganciate 
            // ai cubi, così luci e cubi sono discendenti dallo stesso nodo.
            
scene::IBillboardSceneNode billboard smgr->addBillboardSceneNode(node);
            
billboard->setPosition(vector3df(0, -1430));
            
billboard->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR );
            
billboard->setMaterialTexture(0driver->getTexture("../../media/particle.bmp"));
            
billboard->setMaterialFlag(video::EMF_LIGHTINGfalse);
            
smgr->addLightSceneNode(billboardvector3df(000), video::SColorf(100), lightRadius);

            
billboard smgr->addBillboardSceneNode(node);
            
billboard->setPosition(vector3df(-21, -14, -21));
            
billboard->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR );
            
billboard->setMaterialTexture(0driver->getTexture("../../media/particle.bmp"));
            
billboard->setMaterialFlag(video::EMF_LIGHTINGfalse);
            
smgr->addLightSceneNode(billboardvector3df(000), video::SColorf(010), lightRadius);

            
billboard smgr->addBillboardSceneNode(node);
            
billboard->setPosition(vector3df(21, -14, -21));
            
billboard->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR );
            
billboard->setMaterialTexture(0driver->getTexture("../../media/particle.bmp"));
            
billboard->setMaterialFlag(video::EMF_LIGHTINGfalse);
            
smgr->addLightSceneNode(billboardvector3df(000), video::SColorf(001), lightRadius);
            
// Ogni cubo ha un cubetto orbitante in modo da notare che sono  
            // illuminati anche dalle luci circostanti, non solo luci figlie.
            
node smgr->addCubeSceneNode(5node);
            
node->setPosition(vector3df(0210));
        }

    
smgr->addCameraSceneNode(0vector3df(0,0,-130), vector3df(0,0,0));

    
CMyLightManager myLightManager = new CMyLightManager(smgr);
    
smgr->setLightManager(0); // Avviamo il light management solo quando vogliamo.
    
device->setEventReceiver(myLightManager);

    
int lastFps = -1;

    while(
device->run())
    {
        
driver->beginScene(truetruevideo::SColor(255,100,101,140));
        
smgr->drawAll();
        
guienv->drawAll();
        
driver->endScene();

        
int fps driver->getFPS();
        if(
fps != lastFps)
        {
            
lastFps fps;
            
core::stringw str L"Managed Lights [";
            
str += driver->getName();
            
str += "] FPS:";
            
str += fps;
            
device->setWindowCaption(str.c_str());
        }
    }

    
myLightManager->drop(); // Cancello il riferimento esplicito ogni volta
    
device->drop();
    return 
0;


Versione pdf scaricabile da QUI
 
Rispondi
  


Vai al forum:


Browsing: 1 Ospite(i)