• 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 23 - SMeshBufferHandling
Modi discussione
#1
Tutorial 23: SMeshBufferHandling

[Immagine: 023shot.jpg]

Un tutorial di geoff.
In questo tutorial impareremo come creare una mesh con Irrlicht. Bene iniziamo creando una mappa heightmap con un po' di illuminazione. Con i tasti 1,2,3 possiamo scegliere l'aspetto della mesh, che poi andremo a mettere nel mesh buffer. Tutte le posizioni, normali, ecc.. saranno aggiornate di conseguenza.
OK iniziamo con gli headers (Penso non ci sia nulla da aggiungere)
Codice PHP:
#include <irrlicht.h>
#include "driverChoice.h"

#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif

//Namespaces for the engine
using namespace irr;
using namespace video;
using namespace core;
using namespace scene;
using namespace io;
using namespace gui;
[
php]
Segue il tipo che sarà usato nella funzione che lavorerà ai colori.
[
php]
typedef SColor colour_func(f32 xf32 yf32 z); 
Poi alcune funzioni da usare per colorare i vertici mentre si sta creando la mesh.
Codice PHP:
// Scala di grigi, basata sull'altezza.
SColor grey(f32f32f32 z)
{
    
u32 n = (u32)(255.f z);
    return 
SColor(255nnn);
}

// Interpolazione tra blu e bianco, con rosso aggiunto in una direzione
// e verde nell'altra.
SColor yellow(f32 xf32 yf32)
{
    return 
SColor(255128 + (u32)(127.f x), 128 + (u32)(127.f y), 255);
}

// bianco puro.
SColor white(f32f32f32) { return SColor(255255255255); } 
Il tipo usato dalla funzione che genera la heightmap. Il range delle x e y tra -0.5 e 0.5, e 's' è la scala della heightmap.
Codice PHP:
typedef f32 generate_func(s16 xs16 yf32 s);

// Una funzione interessante :-)
f32 eggbox(s16 xs16 yf32 s)
{
    const 
f32 r 4.f*sqrtf((f32)(x*y*y))/s;
    const 
f32 z expf(-2) * (cosf(0.2f x) + cosf(0.2f y));
    return 
0.25f+0.25f*z;
}

// Una funzione seno :-/
f32 moresine(s16 xs16 yf32 s)
{
    const 
f32 xx=0.3f*(f32)x/s;
    const 
f32 yy=12*y/s;
    const 
f32 z sinf(xx*xx+yy)*sinf(xx+yy*yy);
    return 
0.25f 0.25f z;
}

// Una funzione semplice
f32 justexp(s16 xs16 yf32 s)
{
    const 
f32 xx=6*x/s;
    const 
f32 yy=6*y/s;
    const 
f32 z = (xx*xx+yy*yy);
    return 
0.3f*z*cosf(xx*yy);

Una classe per rappresentare la heightmaps. Molto di quello che segue dovrebbe essere ovvio.
Codice PHP:
class HeightMap
{
private:
    const 
u16 Width;
    const 
u16 Height;
    
f32 s;
    
core::array<f32data;
public:
    
HeightMap(u16 _wu16 _h) : Width(_w), Height(_h), s(0.f), data(0)
    {
        
sqrtf((f32)(Width Width Height Height));
        
data.set_used(Width Height);
    }

    
// Riempio la heightmap con il valore generato da f.
    
void generate(generate_func f)
    {
        
u32 i=0;
        for(
u16 y 0Height; ++y)
            for(
u16 x 0Width; ++x)
                
set(i++, calc(fxy));
    }

    
u16 height() const { return Height; }
    
u16 width() const { return Width; }

    
f32 calc(generate_func fu16 xu16 y) const
    {
        const 
f32 xx = (f32)Width*0.5f;
        const 
f32 yy = (f32)Height*0.5f;
        return 
f((u16)xx, (u16)yys);
    }

    
// L'altezza a (x, y) è la posizione di y * larghezza + x.

    
void set(u16 xu16 yf32 z) { data[Width x] = z; }
    
void set(u32 if32 z) { data[i] = z; }
    
f32 get(u16 xu16 y) const { return data[Width x]; } 
L'unica parte difficile. Qui consideriamo la normale in (x, y) come il prodotto vettoriale dei due vettori tra il punto adiacente alla direzione orizzontale e la verticale.
's' è il fattore di scala che è necessario indicare se le unità dell'altezza con devono corrispondere a quelle delle ampiezze; per esempio, se la mappa ha l'altezza espressa in metri e le coordinate espresse in chilometri.
Codice PHP:
vector3df getnormal(u16 xu16 yf32 s) const
    {
        const 
f32 zc get(xy);
        
f32 zlzrzuzd;

        if (
== 0)
        {
            
zr get(1y);
            
zl zc zc zr;
        }
        else if (
== Width 1)
        {
            
zl get(1y);
            
zr zc zc zl;
        }
        else
        {
            
zr get(1y);
            
zl get(1y);
        }

        if (
== 0)
        {
            
zd get(x1);
            
zu zc zc zd;
        }
        else if (
== Height 1)
        {
            
zu get(x1);
            
zd zc zc zu;
        }
        else
        {
            
zd get(x1);
            
zu get(x1);
        }

        return 
vector3df(* (zl zr), 4* (zd zu)).normalize();
    }
}; 
La classe che genera la mesh dalla heightmap.
Codice PHP:
class TMesh
{
private:
    
u16 Width;
    
u16 Height;
    
f32 Scale;
public:
    
SMeshMesh;

    
TMesh() : Mesh(0), Width(0), Height(0), Scale(1.f)
    {
        
Mesh = new SMesh();
    }

    ~
TMesh()
    {
        
Mesh->drop();
    }

    
// A meno di una heightmap piccola, non entrerà tutta in una singola mesh
    // SMeshBuffer. Questa funzione taglia a pezzetti la mappa e genera un mesh
    // buffer per ciascuno di essi.

    
void init(const HeightMap &hmf32 scalecolour_func cfIVideoDriver *driver)
    {
        
Scale scale;

        const 
u32 mp driver -> getMaximalPrimitiveCount();
        
Width hm.width();
        
Height hm.height();
        
        const 
u32 sw mp / (Height); // la larghezza di ciascun pezzettoe

        
u32 i=0;
        for(
u32 y0 0y0 Heighty0 += sw)
        {
            
u16 y1 y0 sw;
            if (
y1 >= Height)
                
y1 Height 1// L'ultimo potrebbe essere più stretto
            
addstrip(hmcfy0y1i);
            ++
i;
        }
        if (
i<Mesh->getMeshBufferCount())
        {
            
// puliamo il restante
            
for (u32 j=ij<Mesh->getMeshBufferCount(); ++j)
            {
                
Mesh->getMeshBuffer(j)->drop();
            }
            
Mesh->MeshBuffers.erase(i,Mesh->getMeshBufferCount()-i);
        }
        
// imposto il flag dirty per essere certo che la copia del buffer
        // si aggiorni, vedere IMesh::setHardwareMappingHint
        
Mesh->setDirty();
        
Mesh->recalculateBoundingBox();
    }

    
// Genero un SMeshBuffer che rappresenta tutti i vertici e indici di valori 
    // delle y tra y0 e y1, e li aggiungo alla mesh.

    
void addstrip(const HeightMap &hmcolour_func cfu16 y0u16 y1u32 bufNum)
    {
        
SMeshBuffer *buf 0;
        if (
bufNum<Mesh->getMeshBufferCount())
        {
            
buf = (SMeshBuffer*)Mesh->getMeshBuffer(bufNum);
        }
        else
        {
            
// create new buffer
            
buf = new SMeshBuffer();
            
Mesh->addMeshBuffer(buf);
            
// to simplify things we drop here but continue using buf
            
buf->drop();
        }
        
buf->Vertices.set_used((y1 y0) * Width);

        
u32 i=0;
        for (
u16 y y0<= y1; ++y)
        {
            for (
u16 x 0Width; ++x)
            {
                const 
f32 z hm.get(xy);
                const 
f32 xx = (f32)x/(f32)Width;
                const 
f32 yy = (f32)y/(f32)Height;

                
S3DVertexbuf->Vertices[i++];
                
v.Pos.set(xScale zy);
                
v.Normal.set(hm.getnormal(xyScale));
                
v.Color=cf(xxyyz);
                
v.TCoords.set(xxyy);
            }
        }

        
buf->Indices.set_used(* (Width 1) * (y1 y0));
        
i=0;
        for(
u16 y y0y1; ++y)
        {
            for(
u16 x 0Width 1; ++x)
            {
                const 
u16 n = (y-y0) * Width x;
                
buf->Indices[i]=n;
                
buf->Indices[++i]=Width;
                
buf->Indices[++i]=Width 1;
                
buf->Indices[++i]=Width 1;
                
buf->Indices[++i]=1;
                
buf->Indices[++i]=n;
                ++
i;
            }
        }

        
buf->recalculateBoundingBox();
    }
}; 
La nostra implementazione per un event receiver, ripreso dal tutorial 4.
Codice PHP:
class MyEventReceiver : public IEventReceiver
{
public:
    
// Il metodo che dobbiamo implementare
    
virtual bool OnEvent(const SEventevent)
    {
        
// Ricordiamo se il tasto è premuto o no
        
if (event.EventType == irr::EET_KEY_INPUT_EVENT)
            
KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;

        return 
false;
    }

    
// Questo serve a capire se il tasto è tenuto premuto
    
virtual bool IsKeyDown(EKEY_CODE keyCode) const
    {
        return 
KeyIsDown[keyCode];
    }

    
MyEventReceiver()
    {
        for (
u32 i=0i<KEY_KEY_CODES_COUNT; ++i)
            
KeyIsDown[i] = false;
    }

private:
    
// Usiamo un array per tenere traccia dello stati di ciascun tasto
    
bool KeyIsDown[KEY_KEY_CODES_COUNT];
}; 
Molto del codice è ripreso da altri esempi. Semplicemente creiamo la mesh dalla heightmap, la illuminiamo con una luce in movimento, e permettiamo all'utente di muovercisi sopra.
Codice PHP:
int main(int argccharargv[])
{
    
// ask user for driver
    
video::E_DRIVER_TYPE driverType=driverChoiceConsole();
    if (
driverType==video::EDT_COUNT)
        return 
1;

    
MyEventReceiver receiver;
    
IrrlichtDevicedevice createDevice(driverType,
            
core::dimension2du(800600), 32falsefalsefalse,
            &
receiver);

    if(
device == 0)
        return 
1;
 
    
IVideoDriver *driver device->getVideoDriver();
    
ISceneManager *smgr device->getSceneManager();
    
device->setWindowCaption(L"Irrlicht Example for SMesh usage."); 
Creiamo la mesh personalizzata e inizializziamola con la heightmap
Codice PHP:
TMesh mesh;
    
HeightMap hm HeightMap(255255);
    
hm.generate(eggbox);
    
mesh.init(hm50.fgreydriver);

    
// Add the mesh to the scene graph
    
IMeshSceneNodemeshnode smgr -> addMeshSceneNode(mesh.Mesh);
    
meshnode->setMaterialFlag(video::EMF_BACK_FACE_CULLINGfalse);

    
// light is just for nice effects
    
ILightSceneNode *node smgr->addLightSceneNode(0vector3df(0,100,0),
        
SColorf(1.0f0.6f0.7f1.0f), 500.0f);
    if (
node)
    {
        
node->getLightData().Attenuation.set(0.f1.f/500.f0.f);
        
ISceneNodeAnimatoranim smgr->createFlyCircleAnimator(vector3df(0,150,0),250.0f);
        if (
anim)
        {
            
node->addAnimator(anim);
            
anim->drop();
        }
    }

    
ICameraSceneNodecamera smgr->addCameraSceneNodeFPS();
    if (
camera)
    {
        
camera->setPosition(vector3df(-20.f150.f, -20.f));
        
camera->setTarget(vector3df(200.f, -80.f150.f));
        
camera->setFarValue(20000.0f);
    } 
Il solito render loop con la gestione degli eventi. La mesh personalizzata e ovviamente parte della grafo di scena che sarà renderizzato con la drawAll.
Codice PHP:
while(device->run())
    {
        if(!
device->isWindowActive())
        {
            
device->sleep(100);
            continue;
        }

        if(
receiver.IsKeyDown(irr::KEY_KEY_W))
        {
            
meshnode->setMaterialFlag(video::EMF_WIREFRAME, !meshnode->getMaterial(0).Wireframe);
        }
        else if(
receiver.IsKeyDown(irr::KEY_KEY_1))
        {
            
hm.generate(eggbox);
            
mesh.init(hm50.fgreydriver);
        }
        else if(
receiver.IsKeyDown(irr::KEY_KEY_2))
        {
            
hm.generate(moresine);
            
mesh.init(hm50.fyellowdriver);
        }
        else if(
receiver.IsKeyDown(irr::KEY_KEY_3))
        {
            
hm.generate(justexp);
            
mesh.init(hm50.fyellowdriver);
        }

        
driver->beginScene(truetrueSColor(0xff000000));
        
smgr->drawAll();
        
driver->endScene();
    }

    
device->drop();
    return 
0;

Fatto, compiliamo e giochiamo col programma.

Versione pdf scaricabile da QUI
 
Reply
  


Vai al forum:


Browsing: 1 Ospite(i)