• 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 16 - Quake3 Map Shader Support
#1
Tutorial 16: Quake3 Map Shader Support

[Immagine: 016shot.jpg]

Questo tutorial mostra come si carica una mappa di Quake 3, come si crea un nodo della scena per ottimizzare il rendering e come si crea una camera controllata dall'utente.
Partiamo come con l'esempio di HelloWorld: Includiamo i il file header di irrlicht e quello necessario a fare le richieste utente sul driver da usare attraverso la console.
Codice PHP:
#include <irrlicht.h>
#include "driverChoice.h" 
Definiamo quale mappa di Quake3 vogliamo che sia caricata attraverso un po' di costanti.
Codice PHP:
#define IRRLICHT_QUAKE3_ARENA
//#define ORIGINAL_QUAKE3_ARENA
//#define CUSTOM_QUAKE3_ARENA
//#define SHOW_SHADER_NAME

#ifdef ORIGINAL_QUAKE3_ARENA
    #define QUAKE3_STORAGE_FORMAT   addFolderFileArchive
    #define QUAKE3_STORAGE_1        "/baseq3/"
    #ifdef CUSTOM_QUAKE3_ARENA
        #define QUAKE3_STORAGE_2    "/cf/"
        #define QUAKE3_MAP_NAME     "maps/cf.bsp"
    #else
        #define QUAKE3_MAP_NAME         "maps/q3dm8.bsp"
    #endif
#endif

#ifdef IRRLICHT_QUAKE3_ARENA
    #define QUAKE3_STORAGE_FORMAT   addFileArchive
    #define QUAKE3_STORAGE_1    "../../media/map-20kdm2.pk3"
    #define QUAKE3_MAP_NAME         "maps/20kdm2.bsp"
#endif

using namespace irr;
using namespace scene
E ancora, per utilizzare la libreria Irrlicht.DLL, dobbiamo linkare il file theIrrlicht.lib. Potremmo utilizzare le opzioni del progetto, ma è più semplice farlo tramite una pragma comment lib:
Codice PHP:
#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif 
Una classe per produrre una serie di screenshots
Codice PHP:
class CScreenShotFactory : public IEventReceiver
{
public:

    
CScreenShotFactoryIrrlichtDevice *device, const c8 templateNameISceneNodenode )
        : 
Device(device), Number(0), FilenameTemplate(templateName), Node(node)
    {
        
FilenameTemplate.replace '/''_' );
        
FilenameTemplate.replace '\\''_' );
    }

    
bool OnEvent(const SEventevent)
    {
        
// check if user presses the key F9
        
if ((event.EventType == EET_KEY_INPUT_EVENT) &&
                
event.KeyInput.PressedDown)
        {
            if (
event.KeyInput.Key == KEY_F9)
            {
                
video::IImageimage Device->getVideoDriver()->createScreenShot();
                if (
image)
                {
                    
c8 buf[256];
                    
snprintf(buf256"%s_shot%04d.jpg",
                            
FilenameTemplate.c_str(),
                            ++
Number);
                    
Device->getVideoDriver()->writeImageToFile(imagebuf85 );
                    
image->drop();
                }
            }
            else
            if (
event.KeyInput.Key == KEY_F8)
            {
                if (
Node->isDebugDataVisible())
                    
Node->setDebugDataVisible(scene::EDS_OFF);
                else
                    
Node->setDebugDataVisible(scene::EDS_BBOX_ALL);
            }
        }
        return 
false;
    }

private:
    
IrrlichtDevice *Device;
    
u32 Number;
    
core::stringc FilenameTemplate;
    
ISceneNodeNode;
}; 
Ok, iniziamo.
Codice PHP:
int IRRCALLCONV main(int argccharargv[])

Come nell'esempio HelloWorld, creiamo un device di Irrlicht tramite createDevice(). La differenza è che ora chiederemo all'utente di selezionare quale accellerazione hardware vuole usare. Il device di tipo Software potrebbe essere troppo lento per gestire una mappa molto grossa di Quake 3, ma giusto per puro divertimento decidiamo di rendere possibile questa scelta.
Codice PHP:
// ask user for driver
    
video::E_DRIVER_TYPE driverType=driverChoiceConsole();
    if (
driverType==video::EDT_COUNT)
        return 
1;

    
// create device and exit if creation failed
    
const core::dimension2du videoDim(800,600);

    
IrrlichtDevice *device createDevice(driverTypevideoDim32false );

    if (
device == 0)
        return 
1// could not create selected driver.

    
const charmapname=0;
    if (
argc>2)
        
mapname argv[2];
    else
        
mapname QUAKE3_MAP_NAME
Prendiamoci i puntatori al video driver e quello allo SceneManager in modo da non dovere scrivere sempre device->getVideoDriver() e device->getSceneManager().
Codice PHP:
video::IVideoDriverdriver device->getVideoDriver();
    
scene::ISceneManagersmgr device->getSceneManager();
    
gui::IGUIEnvironmentgui device->getGUIEnvironment();

    
device->getFileSystem()->addFileArchive("../../media/"); 
Per mostrare la mappa Quake 3 prima dobbiamo caricarla. Le mappe Quake 3 sono registrate dentro a file .pk3, che non sono altro che file .zip. Quindi aggiungiamo il file .pk3 nel nostro FileSystem. Dopo averlo aggiunto saremo in grado di leggere da questo archivio come se fosse una qualsiasi file registrato su disco.
Codice PHP:
if (argc>2)
        
device->getFileSystem()->QUAKE3_STORAGE_FORMAT(argv[1]);
    else
        
device->getFileSystem()->QUAKE3_STORAGE_FORMAT(QUAKE3_STORAGE_1);
#ifdef QUAKE3_STORAGE_2
    
device->getFileSystem()->QUAKE3_STORAGE_FORMAT(QUAKE3_STORAGE_2);
#endif

    // Quake3 Shader controls Z-Writing
    
smgr->getParameters()->setAttribute(scene::ALLOW_ZWRITE_ON_TRANSPARENTtrue); 
Ora possiamo caricare la mesh della mappa richiamando getMesh(). Riceviamo di ritorno un puntatore ad un IAnimatedMesh. Come sapete le mappe di Quake 3 non sono veramente animate, si tratta di una grossa serie di geometrie statiche con attaccati i rispettivi materiali. In questo caso la IAnimated mesh consiste sono di un frame, quindi ci andremo a prendere la prima e unica “animazione” contenuta, che proprio il nostro livello e ci creiamo sopra una scena Octree, usando la addOctreeSceneNode(). Gli alberi Octree ottimizzano la scena cercando di disegnare solo quelle geometrie che sono visibili. In alternativa al Octree si può sempre usare AnimatedMeshSceneNode, che però disegnerà sempre l'intera geometria del livello, senza alcuna ottimizzazione. Provate: scrivete addAnimatedMeshSceneNode al posto di addOctreeSceneNode e confrontate il disegno delle primitive tramite il video driver. (Esiste il metodo getPrimitiveCountDrawed() della classe IVideoDriver). Notare che l'ottimizzazione con gli Octree ha senso solo se usata su mesh enormi composte da molte geometrie al loro interno.
Codice PHP:
scene::IQ3LevelMesh* const mesh =
        (
scene::IQ3LevelMesh*) smgr->getMesh(mapname); 
aggiungiamo la geometria della mesh alla scena (poligoni & patches) la geometria della mesh è ottimizzata per un disegno più veloce ora.
Codice PHP:
scene::ISceneNodenode 0;
    if (
mesh)
    {
        
scene::IMesh * const geometry mesh->getMesh(quake3::E_Q3_MESH_GEOMETRY);
        
node smgr->addOctreeSceneNode(geometry0, -14096);
    }

    
// create an event receiver for making screenshots
    
CScreenShotFactory screenshotFactory(devicemapnamenode);
    
device->setEventReceiver(&screenshotFactory); 
ora costruiamoci un nodo per ciascun shader, l'oggetto è registrato nella mesh del livello come scene::E_Q3_MESH_ITEMS e l'ID dello Shader è registrato nei MaterialParameters, per lo più oscuri, lava in movimento o.. tubi verdi lampeggianti?
Codice PHP:
if ( mesh )
    {
        
// the additional mesh can be quite huge and is unoptimized
        
const scene::IMesh * const additional_mesh mesh->getMesh(quake3::E_Q3_MESH_ITEMS);

#ifdef SHOW_SHADER_NAME
        
gui::IGUIFont *font device->getGUIEnvironment()->getFont("../../media/fontlucida.png");
        
u32 count 0;
#endif

        
for ( u32 i 0i!= additional_mesh->getMeshBufferCount(); ++)
        {
            const 
IMeshBuffermeshBuffer additional_mesh->getMeshBuffer(i);
            const 
video::SMaterialmaterial meshBuffer->getMaterial();
            
// The ShaderIndex is stored in the material parameter
            
const s32 shaderIndex = (s32material.MaterialTypeParam2;

            
// the meshbuffer can be rendered without additional support, or it has no shader
            
const quake3::IShader *shader mesh->getShader(shaderIndex);
            if (
== shader)
            {
                continue;
            }
            
// we can dump the shader to the console in its
            // original but already parsed layout in a pretty
            // printers way.. commented out, because the console
            // would be full...
            // quake3::dumpShader ( Shader );

            
node smgr->addQuake3SceneNode(meshBuffershader);

#ifdef SHOW_SHADER_NAME
            
count += 1;
            
core::stringw namenode->getName() );
            
node smgr->addBillboardTextSceneNode(
                    
fontname.c_str(), node,
                    
core::dimension2d<f32>(80.0f8.0f),
                    
core::vector3df(0100));
#endif
        
}
    } 
Non ci serve altro che una Camera per vedere dentro alla nostra mappa Quake 3. Vogliamo che sia una camera controlalta dall'utente. In Irrlicht ne abbiamo varie disponibili. Per esempio la Maya Camera i il cui controllo è simile a quella presente nel software Maya: Rotazione con la pressione del tasto sinistro del mouse, Zoom con tutti e due i tasti premuti, traslazione con la pressione del tasto detro. La creiamo con addCameraSceneNodeMaya(). Nel nostro esempio però, vogliamo creare una camera di tipo analogo a quello dei giochi first person shooter games (FPS).
Codice PHP:
scene::ICameraSceneNodecamera smgr->addCameraSceneNodeFPS(); 
adesso ci serve un buon punto da dove partire dentro al livello. Possiamo ottenere dal loader integrato di Irrlicht per le mappe Quake3 tutti i punti chiamati "info_player_deathmatch" tra questi ne scegliamo uno random.
Codice PHP:
if ( mesh )
    {
        
quake3::tQ3EntityList &entityList mesh->getEntityList();

        
quake3::IEntity search;
        
search.name "info_player_deathmatch";

        
s32 index entityList.binary_search(search);
        if (
index >= 0)
        {
            
s32 notEndList;
            do
            {
                const 
quake3::SVarGroup *group entityList[index].getGroup(1);

                
u32 parsepos 0;
                const 
core::vector3df pos =
                    
quake3::getAsVector3df(group->get("origin"), parsepos);

                
parsepos 0;
                const 
f32 angle quake3::getAsFloat(group->get("angle"), parsepos);

                
core::vector3df target(0.f0.f1.f);
                
target.rotateXZBy(angle);

                
camera->setPosition(pos);
                
camera->setTarget(pos target);

                ++
index;
                  
notEndList = ( index < (s32entityList.size () && entityList[index].name == search.name && (device->getTimer()->getRealTime() >> ) & );
                
notEndList index == 2;
            } while ( 
notEndList );
        }
    } 
Rendiamo invisibile il cursore del mouse.
Codice PHP:
device->getCursorControl()->setVisible(false);

    
// load the engine logo
    
gui->addImage(driver->getTexture("irrlichtlogo2.png"),
            
core::position2d<s32>(1010));

    
// show the driver logo
    
const core::position2di pos(videoDim.Width 128videoDim.Height 64);

    switch ( 
driverType )
    {
        case 
video::EDT_BURNINGSVIDEO:
            
gui->addImage(driver->getTexture("burninglogo.png"), pos);
            break;
        case 
video::EDT_OPENGL:
            
gui->addImage(driver->getTexture("opengllogo.png"), pos);
            break;
        case 
video::EDT_DIRECT3D8:
        case 
video::EDT_DIRECT3D9:
            
gui->addImage(driver->getTexture("directxlogo.png"), pos);
            break;
    } 
Abbiamo fatto tutto, ora disegnamo. Mostriamo anche i frame per secondo e il numero di primitive disegnate sul titolo della finestra. La linea 'if (device->isWindowActive())' è opzionale, ma serve ad evitare il riposizionamento del mouse da parte dell'engine quando si passa da una finestra attiva ad un altra.
Codice PHP:
int lastFPS = -1;

    while(
device->run())
    if (
device->isWindowActive())
    {
        
driver->beginScene(truetruevideo::SColor(255,20,20,40));
        
smgr->drawAll();
        
gui->drawAll();
        
driver->endScene();

        
int fps driver->getFPS();
        
//if (lastFPS != fps)
        
{
            
io::IAttributes * const attr smgr->getParameters();
            
core::stringw str L"Q3 [";
            
str += driver->getName();
            
str += "] FPS:";
            
str += fps;
#ifdef _IRR_SCENEMANAGER_DEBUG          
            
str += " Cull:";
            
str += attr->getAttributeAsInt("calls");
            
str += "/";
            
str += attr->getAttributeAsInt("culled");
            
str += " Draw: ";
            
str += attr->getAttributeAsInt("drawn_solid");
            
str += "/";
            
str += attr->getAttributeAsInt("drawn_transparent");
            
str += "/";
            
str += attr->getAttributeAsInt("drawn_transparent_effect");
#endif
            
device->setWindowCaption(str.c_str());
            
lastFPS fps;
        }
    } 
Alla fine cancelliamo il device Irrlicht.
Codice PHP:
device->drop();

    return 
0;


Versione pdf scaricabile da QUI
 
Rispondi
  


Discussioni simili
Discussione Autore Risposte Letto Ultimo messaggio
  Tutorial 21b - Quake3 Explorer Chip 0 536 23-08-2015, 11:32 AM
Ultimo messaggio: Chip
  Tutorial 21a - Quake3 Explorer Chip 0 533 23-08-2015, 11:25 AM
Ultimo messaggio: Chip

Vai al forum:


Browsing: 1 Ospite(i)