24-08-2015, 11:25 AM
(Questo messaggio è stato modificato l'ultima volta il: 24-08-2015, 11:40 AM da Chip.)
Tutorial 23: SMeshBufferHandling
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)
Poi alcune funzioni da usare per colorare i vertici mentre si sta creando la mesh.
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.
Una classe per rappresentare la heightmaps. Molto di quello che segue dovrebbe essere ovvio.
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.
La classe che genera la mesh dalla heightmap.
La nostra implementazione per un event receiver, ripreso dal tutorial 4.
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.
Creiamo la mesh personalizzata e inizializziamola con la heightmap
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.
Fatto, compiliamo e giochiamo col programma.
Versione pdf scaricabile da QUI
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 x, f32 y, f32 z);
Codice PHP:
// Scala di grigi, basata sull'altezza.
SColor grey(f32, f32, f32 z)
{
u32 n = (u32)(255.f * z);
return SColor(255, n, n, n);
}
// Interpolazione tra blu e bianco, con rosso aggiunto in una direzione
// e verde nell'altra.
SColor yellow(f32 x, f32 y, f32)
{
return SColor(255, 128 + (u32)(127.f * x), 128 + (u32)(127.f * y), 255);
}
// bianco puro.
SColor white(f32, f32, f32) { return SColor(255, 255, 255, 255); }
Codice PHP:
typedef f32 generate_func(s16 x, s16 y, f32 s);
// Una funzione interessante :-)
f32 eggbox(s16 x, s16 y, f32 s)
{
const f32 r = 4.f*sqrtf((f32)(x*x + y*y))/s;
const f32 z = expf(-r * 2) * (cosf(0.2f * x) + cosf(0.2f * y));
return 0.25f+0.25f*z;
}
// Una funzione seno :-/
f32 moresine(s16 x, s16 y, f32 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 x, s16 y, f32 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);
}
Codice PHP:
class HeightMap
{
private:
const u16 Width;
const u16 Height;
f32 s;
core::array<f32> data;
public:
HeightMap(u16 _w, u16 _h) : Width(_w), Height(_h), s(0.f), data(0)
{
s = 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 = 0; y < Height; ++y)
for(u16 x = 0; x < Width; ++x)
set(i++, calc(f, x, y));
}
u16 height() const { return Height; }
u16 width() const { return Width; }
f32 calc(generate_func f, u16 x, u16 y) const
{
const f32 xx = (f32)x - Width*0.5f;
const f32 yy = (f32)y - Height*0.5f;
return f((u16)xx, (u16)yy, s);
}
// L'altezza a (x, y) è la posizione di y * larghezza + x.
void set(u16 x, u16 y, f32 z) { data[y * Width + x] = z; }
void set(u32 i, f32 z) { data[i] = z; }
f32 get(u16 x, u16 y) const { return data[y * Width + x]; }
'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 x, u16 y, f32 s) const
{
const f32 zc = get(x, y);
f32 zl, zr, zu, zd;
if (x == 0)
{
zr = get(x + 1, y);
zl = zc + zc - zr;
}
else if (x == Width - 1)
{
zl = get(x - 1, y);
zr = zc + zc - zl;
}
else
{
zr = get(x + 1, y);
zl = get(x - 1, y);
}
if (y == 0)
{
zd = get(x, y + 1);
zu = zc + zc - zd;
}
else if (y == Height - 1)
{
zu = get(x, y - 1);
zd = zc + zc - zu;
}
else
{
zd = get(x, y + 1);
zu = get(x, y - 1);
}
return vector3df(s * 2 * (zl - zr), 4, s * 2 * (zd - zu)).normalize();
}
};
Codice PHP:
class TMesh
{
private:
u16 Width;
u16 Height;
f32 Scale;
public:
SMesh* Mesh;
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 &hm, f32 scale, colour_func cf, IVideoDriver *driver)
{
Scale = scale;
const u32 mp = driver -> getMaximalPrimitiveCount();
Width = hm.width();
Height = hm.height();
const u32 sw = mp / (6 * Height); // la larghezza di ciascun pezzettoe
u32 i=0;
for(u32 y0 = 0; y0 < Height; y0 += sw)
{
u16 y1 = y0 + sw;
if (y1 >= Height)
y1 = Height - 1; // L'ultimo potrebbe essere più stretto
addstrip(hm, cf, y0, y1, i);
++i;
}
if (i<Mesh->getMeshBufferCount())
{
// puliamo il restante
for (u32 j=i; j<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 &hm, colour_func cf, u16 y0, u16 y1, u32 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((1 + y1 - y0) * Width);
u32 i=0;
for (u16 y = y0; y <= y1; ++y)
{
for (u16 x = 0; x < Width; ++x)
{
const f32 z = hm.get(x, y);
const f32 xx = (f32)x/(f32)Width;
const f32 yy = (f32)y/(f32)Height;
S3DVertex& v = buf->Vertices[i++];
v.Pos.set(x, Scale * z, y);
v.Normal.set(hm.getnormal(x, y, Scale));
v.Color=cf(xx, yy, z);
v.TCoords.set(xx, yy);
}
}
buf->Indices.set_used(6 * (Width - 1) * (y1 - y0));
i=0;
for(u16 y = y0; y < y1; ++y)
{
for(u16 x = 0; x < Width - 1; ++x)
{
const u16 n = (y-y0) * Width + x;
buf->Indices[i]=n;
buf->Indices[++i]=n + Width;
buf->Indices[++i]=n + Width + 1;
buf->Indices[++i]=n + Width + 1;
buf->Indices[++i]=n + 1;
buf->Indices[++i]=n;
++i;
}
}
buf->recalculateBoundingBox();
}
};
Codice PHP:
class MyEventReceiver : public IEventReceiver
{
public:
// Il metodo che dobbiamo implementare
virtual bool OnEvent(const SEvent& event)
{
// 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=0; i<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];
};
Codice PHP:
int main(int argc, char* argv[])
{
// ask user for driver
video::E_DRIVER_TYPE driverType=driverChoiceConsole();
if (driverType==video::EDT_COUNT)
return 1;
MyEventReceiver receiver;
IrrlichtDevice* device = createDevice(driverType,
core::dimension2du(800, 600), 32, false, false, false,
&receiver);
if(device == 0)
return 1;
IVideoDriver *driver = device->getVideoDriver();
ISceneManager *smgr = device->getSceneManager();
device->setWindowCaption(L"Irrlicht Example for SMesh usage.");
Codice PHP:
TMesh mesh;
HeightMap hm = HeightMap(255, 255);
hm.generate(eggbox);
mesh.init(hm, 50.f, grey, driver);
// Add the mesh to the scene graph
IMeshSceneNode* meshnode = smgr -> addMeshSceneNode(mesh.Mesh);
meshnode->setMaterialFlag(video::EMF_BACK_FACE_CULLING, false);
// light is just for nice effects
ILightSceneNode *node = smgr->addLightSceneNode(0, vector3df(0,100,0),
SColorf(1.0f, 0.6f, 0.7f, 1.0f), 500.0f);
if (node)
{
node->getLightData().Attenuation.set(0.f, 1.f/500.f, 0.f);
ISceneNodeAnimator* anim = smgr->createFlyCircleAnimator(vector3df(0,150,0),250.0f);
if (anim)
{
node->addAnimator(anim);
anim->drop();
}
}
ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS();
if (camera)
{
camera->setPosition(vector3df(-20.f, 150.f, -20.f));
camera->setTarget(vector3df(200.f, -80.f, 150.f));
camera->setFarValue(20000.0f);
}
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(hm, 50.f, grey, driver);
}
else if(receiver.IsKeyDown(irr::KEY_KEY_2))
{
hm.generate(moresine);
mesh.init(hm, 50.f, yellow, driver);
}
else if(receiver.IsKeyDown(irr::KEY_KEY_3))
{
hm.generate(justexp);
mesh.init(hm, 50.f, yellow, driver);
}
driver->beginScene(true, true, SColor(0xff000000));
smgr->drawAll();
driver->endScene();
}
device->drop();
return 0;
}
Versione pdf scaricabile da QUI