PDA

Visualizza la versione completa : [C++] Template: qualche informazione


giuseppe500
01-05-2009, 23:27
ciao.
non riesco a farmi entrare in testa i template , ho questa situazione:

1)una classe importer che carica i dati da un file,e li mette in una classe che erdita da CBase
2)Cbase ha un array di VertexBase
3)da VertexBase derivano vari tipi di vertici che differiscono l'uno dall'altro per qualche campo
4)Vorrei che importer caricasse una classe cbase differente per il tipo di vertice e il tipo di struttura importazione per un parametro passato :
ad es importer<CMesh> o importer<CSkinnedMesh> deve fare due importazioni legermente diverse per struttura e per tipo VertexBase.
e CMesh e CSkinnedMesh derivano da CBase
5)all' interno di importer ci sono varie funzioni ed array che vengono utilizzate o meno a seconda del tipo di classeCBase implementata , è meglio ereditare da importer e implementare le nuove funzioni?
coi template posso creare due tipi di importer a seconda del tipo ? è cosi' che si fa?

voi come impostereste il progetto?
grazie.

giuseppe500
02-05-2009, 15:24
per tipi di vertici diversi si puo' utilizzare quella che è chiamata specializzazione ?
non sono pero' riuscito a capirla bene.

shodan
02-05-2009, 18:27
CMesh e CSkinnedMesh derivano da CBase

Derivate come? CBase è un'interfaccia con funzioni virtuali astratte? E CMesh e CSkinnedMesh reimplementano queste funzioni?

Perché non vedo in che modo i template possano essere d'aiuto.

giuseppe500
03-05-2009, 00:54
non so , chiedevo dato che mi stavo leggendo lo Stroustrup ed ero sui template mi era parsa l'idea di utilizzare i template che , tra l'altro non sono riuscito a comprendere a fondo(cosa fanno che le classi normali con l'ereditarietà , il polimorfismo e l'incapsulamento non riescono a fare?
e perchè utilizzarli?)

cerco di spiegarmi decentemente dato che in questo non sono un campione:
Ho creato un importatore da 3ds , collada e blender per il mio piccolo engine 3d , utilizzando le directx 10.

1)Ho una classe CIntermediateMesh , una classe importer e una classe CMesh .
la classe importer importa ad es da collada tutti i possibili dati e proprietà del file .dae(collada)
in una serie di strutture e array della classe CIntermediateMesh , i dati pero' sono appunto dati intermedi , ossia ad es ho i vertici e le loro proprietà relative (posizione , normale, tangenti ecc..,)per le texture ho il path del file e le coordinate UV invece che una risorsa pronta ad essere utilizzata dallo shader(che alla fine è quello che prende i vertici ed effettua le trasformazioni o il codice per il disegno (colore , luci ecc...).
Mi serve avere una classe intermedia per poter serializzare con boost i dati dato che l'importatore collada ci mette molto tempo a processare tutte le proprietà ed è un formato lento da caricare, tutti i dati nella classe intermedia sono dati primitivi e vector , in modo da permettere a boost la serializzazione.
e non posso utilizzare la classe CMesh(la classe pronta per il disegno ) perchè ci sono tipi non primitivi che non possono essere serializzati : risorse e D3DXMATRIX ad es per intenderci.

2)ora quello che non ho ancora implementato è la classe che trasforma dall' intermedia alla "pronta" per il disegno , ma non dovrebbe essere difficile.
non so' se è buona come idea utilizzare una classe a se stante solo per questa trasformazione e chiedo quindio a voi.

3)vertici
Alla fine una classe CMesh avra' 2 buffer di directx(ID3D10Buffer* m_pVertexBuffer; per i vertici e ID3D10Buffer* m_pIndexBuffer; per gli indici) con alcune altre classi(sempre del tipo ID3D...) per le risorse(texture ecc....)
ora per creare i buffer ho bisogno di una struttura vertex che passa allo shader i dati che verranno processati , lo shader ha come "interfaccia" in pratica una struttura dati particolare a seconda dei dati di cui ha bisogno per il suo "lavoro" ad es:



struct SimpleVertex
{
D3DXVECTOR3 Pos; // Position
D3DXVECTOR2 Tex; // Texture Coordinate
};


lo shader utilizza questa dichiarazione (semplificando , ci sarebbero anche i layout e altre cose ma in pratica la logica è questa)per fare il binding dei dati .

4)ora in un progettino ad es un piccolo gioco avro' vari shader e vari tipi di vertici in quanto ad es una mesh statica non ha bisogno dell index dei bones , mentre ad es una skinned mesh si e cosi via .
una mesh statica avra quindi bisogno per essere renderizzata di una sua struttura vertice e di un suo shader che prende i campi di quei vertici e li utilizza.

5)la mia idea è quella di mettere tutti gli shader (dei vari tipi di effetti che devono fare)in una collezione e le mesh in un altra collezione , nella classe CModel e

1)ciclare la collezione degli shader
2)predere uno shader e vedere di che tipo di vertice e di effetto ha bisogno
3)prendere ad una ad una le mesh che corrispondono(come vertexbuffer) a quel vertice e all'effetto che vogliono fare e disegnarle tramite lo shader.
ritornare a 2).

non so bene pero' come implementare una regola di corrispondenza tra il tipo di vertice utilizzato dallo shader e il tipo di vertice utilizzato nella mesh , va bene un codice in base 2?
1=1=meshbase con materiale = shader1
10=2=meshbase con materiale e texture= shader2
11=3 = skinned mesh=shader3
ecc...

grazie.

shodan
03-05-2009, 17:55
E' una domanda a cui non si può ripondere bene su un forum.
Generalmente i template servono per risolvere alcune problematiche di programmazione che un approccio polimorfico renderebbe complicate.
Pensa alle classe std::vector. Se non fosse basata su template, occorrerebbe che tutti i dati derivassero
da una classe base comune per poi fare cast a go go per avere il dato corretto.
O a una funzione compare().
Senza template saresti costretto a creare N funzioni in overloading ognuna per il tipo di dato che puoi passare.
Con il template astrai i dato e scrivi una sola funzione.
E' meglio:


bool compare(int a,int b) { return a == b; }
bool compare(char a,char b) { return a == b; }
bool compare(char* a,char* b) { return !strcmp(a,b); }
bool compare(MioTipo a,MioTipo b) { return a == b; }
etc...

o:


template <typename X>
bool compare(X a,X b) { return a == b; }

lasciando che sia il compilatore a scriversi la funzione?
Specializzandolo per char* diventa:


template <>
bool compare<char*>(char* a,char* b) { return !strcmp(a,b); }

la specializzazione serve in caso un tipo ( o più ) di dato su N dati deve avere un comportamento speciale appunto.

Ti consiglio questo articolo:
http://www.eptacom.net/pubblicazioni/pub_it/oopgen.html

Per quanto riguarda il resto.
A quanto ho capito hai una situazione del genere:


class VertexBase {
public:
virtual ~VertexBase() {}
virtual void paint() {}
};

class CBase {
virtual ~Cbase() {}
virtual VertexBase* getVertex(int i) { return vb_[i]; }
virtual void addVertex(VertexBase* vb, int pos) { vb_[pos] = vb;}
protected:
VertexBase vb_[100];
};

class SimpleVertex : public VertexBase {
public:
SimpleVertex(D3DXVECTOR3 a, D3DXVECTOR2 b) { ... setta i membri privati ... }
virtual void paint() {
... fa qualcosa con i dati interni Pos, Tex ...
... e disegna il Vertex ...
}

private:
D3DXVECTOR3 Pos; // Position
D3DXVECTOR2 Tex; // Texture Coordinate
};

class SkinnedVertex : public VertexBase {
public:
SimpleVertex(
D3DXVECTOR3 a,
D3DXVECTOR2 b,
D3DXVECTOR3 c,
D3DXVECTOR2 d
) { ... setta i membri privati ... }

virtual void paint() {
... fa qualcosa con i dati interni Pos, Tex, Pos2, Tex2 ...
... e disegna il Vertex ...
}

private:
D3DXVECTOR3 Pos; // Position
D3DXVECTOR2 Tex; // Texture Coordinate
D3DXVECTOR3 Pos2; // Position
D3DXVECTOR2 Tex2; // Texture Coordinate
};

class CShader {
public:
virtual void rendering(CBase* cb) {}
private:
};

class CMesh: public Cbase {
public:
virtual void getVertex(int pos) {
... eventuali elaborazioni su membri privati di CMesh ...
return vb_[pos];
}

virtual void addVertex(VertexBase* vb, int pos) {
... eventuali elaborazioni su membri privati di CMesh ...
vb_[pos] = vb;
}

private:
};

class CSkinnedMesh: public Cbase {
public:
virtual void getVertex(int pos) {
... eventuali elaborazioni su membri privati di CSkinnedMesh ...
return vb_[pos];
}

virtual void addVertex(VertexBase* vb, int pos) {
... eventuali elaborazioni su membri privati di CSkinnedMesh ...
vb_[pos] = vb;
}
private:
};

class CSimpleShader : public CShader {
public:
virtual void rendering(CBase* cb) {
... eventuale codice specifico per CSimpleShader ...
... cicla su ogni Vertex di CBase ...
cb->getVertex(i)->paint();
}
private:
};

class CSkinnedShader : public CShader {
public:
virtual void rendering(CBase* cb) {
... eventuale codice specifico per CSkinnedShader ...
... cicla su ogni Vertex di CBase ...
cb->getVertex(i)->paint();
}
private:
};

template <typename T> class CImport;

template <>
class CImport<CMesh> {
public:
CBase* load() {
... seleziona i vertex adatti a CMesh ...
VertexBase* vb = new SimpleVertex(dato1, dato2);
CBase* cb = new CMesh;
cb->addVertex(vb);
return cb;
}
};

template <>
class CImport<CSkinnedMesh> {
public:
CBase* load() {
... seleziona i vertex adatti a CSkinnedMesh ...
VertexBase* vb = new SkinnedVertex(dato1, dato2, dato3, dato4);
CBase* cb = new CSkinnedMesh;
cb->addVertex(vb);
return cb;
}
};

CImport<CMesh> msh;
CImport<CSkinnedMesh> skmh;

CShader* ssh = new CSimpleShader;
CShader* sksh = new CSkinnedShader;

ssh->rendering(msh.load());
sksh->rendering(sksh.load());


E' un codice di massima. (E probabilmente non è quello che cercavi :) )
Il concetto alla base è che non è lo Shader che "shaderizza" (perdona il neologismo :) ) ma è il Vertex nella sua specifica implementazione a sapere come "shaderizzarsi".
Lo Shader non fa altro che invocare la getVertex() che restituisce il giusto Vertex

Loading