Ah, okay. Aber Assimp z.B. liegt schon in einer DLL; d.h. wenn da neue Mesh-Formate hinzukommen, braucht man nur die DLL zu ersetzen und hat seine neuen Loader ohne, dass was an der Engine getan wurde. Für Texturen sollte es auch eine Grafikbibliothek des Vertrauens geben, die ebenfalls per DLL verfügbar ist. Und zur Not kapselt man es selber in einem eigenen Modul und bindet den/die benutzten Loader statisch an dieses.
Wiedemauchsei – noch schnell eine Skizze, wie ich es tun würde:
Code: Alles auswählen
namespace resources {
struct EType { enum Value { // Workaround, damit C++’ dumme enums nicht das Namespace verpesten
mesh,
texture,
[…]
number // Anzahl der Typen
}; };
// Basisklasse für Ressourcen, mit allem, was du in allen Ressourcen brauchst. So eine Schnittstelle ist immer nützlich, wenn man Operationen auf alle Ressourcen generell anwenden will – wären diese Daten direkt im Template, wäre das nur mit relativ viel Geschick und einigem Overhead möglich.
class IResource {
protected:
// Kann nur durch eine abgeleitete Klasse, nämlich eine voll typisierte Ressource, instanziert werden …
IResource(…);
};
// Ressource eines bestimmten Typs, je einzeln durch Template-Spezialisierung definiert.
template <
EType itsType
> class CResource;
// Weil es bei der Benutzung einfach bequemer ist!
typedef CResource<EType::mesh> CMesh;
typedef CResource<EType::texture> CTexture;
template <
> class CResource<EType::mesh>
: public IResource
{
private:
CArray<CVertex> * myVertices; // oder vllt sogar eine aiScene?
…
public:
// Der Konstruktor nimmt nur *fertige* Mesh-Daten möglichst roh entgegen und legt sie in der Instanz zusammen. Hier wird nichts geladen, weil es für die Implementierung eines Meshs vollkommen unwichtig ist, was für Dateiformate es da draußen gibt und wie sie aufgebaut sind. Das macht später der Manager.
CResource(
size_t const numberOfVertices,
CVertex const * const itsVertices,
…
)
: myVertices(numberOfVertices, itsVertices)
, …
{ };
};
template <
> class CResource<EType::texture>
: public IResource
{
private:
ilImage myPixels;
…
public:
// Dasselbe wie beim Mesh.
CResource(
byte const * const itsPixels
…
)
: myPixels(itsPixels)
, …
{ };
};
class CManager {
private:
// Wie du die Ressourcen intern speicherst, musst du dir selber überlegen; hängt von deinem Einsatzzweck ab. Einige schwören auf heterogene oder auf assoziative Container; ich persönlich nutze bloß ein homogenes Array von Containern – einer für jeden Ressource-Typ – weil ich immer über alle Ressourcen desselben Typs iteriere und nie einzelne Ressourcen herauspicken muss. Hier nehmen wir mal was ::std::map-mäßiges, um Ressourcen mit ihren Namen adressieren zu können.
CMap<CString, IResource> myResourcesPerType[EType::number];
// Genau wie die Ressource-Klasse definieren wir diese Funktion später per Template-Spezialisierung.
template <
EType::EValue itsType
> CResource<itsType> loadResource(
CString itsName
);
public:
template <
EType::EValue itsType
> CResource<itsType> & getResource(
CString itsName
) {
// Wenn die Ressource-Klasse kein Template wäre, würde das jetzt viele hässliche switch-Statements oder Schleifen bedeuten, weil wir je nach Ressource-Typ eine Funktion loadMesh(), loadTexture() etc aufrufen müssten. Yippie!
// Checken, ob die Ressource schon geladen ist:
if(IResource * const toResult = myResourcesPerType[itsType].find(itsName)) { // Variable in if-Statement deklarieren bedeutet, eine Zeile zu sparen. Instant flamewar!
// Sicherstellen, dass wir im vorherigen Programmverlauf auch tatsächlich nur passend typisierte Ressourcen in dem Array gespeichert haben. Mit heterogenen Containern wäre die ganze Casterei überflüssig, aber ich habe gerade keinen zur Hand.
assert(nullptr != dynamic_cast<CResource<itsType> *>(toResult));
return *dynamic_cast<CResource<itsType> *>(toResult);
}
// Ressource ist noch nicht geladen. Auf, auf!
else {
CResource<itsType> & result = myResourcesPerType[itsType].insert(itsName, loadResource<itsType>(itsName));
return result;
}
}
}; // class CManager
template <
> CMesh CManager::loadResource<EType::model>(
CString itsName
) {
aiScene * toImportedScene = aiLoad("models//" + itsName); // Das fällt natürlich am Ende noch ein wenig komplexer aus, mit Caches oder sonstwas. Außerdem unterstützt die Board-Software keine Backslashes.
return CMesh(toImportedScene->Mesh->NumVertices, toImportedScene->Mesh->Vertices, …);
}
template <
> CTexture CManager::loadResource<EType::texture>(
CString itsName
) {
CImage textureData = MyImageLib::load("textures//" + itsName);
return CTexture(textureData->rawPixels, …);
}
} // namespace resources
resources::CManager manajah;
auto tex = manajah.load<resources::EType::texture>("Blütenzauber");